# Deploy a Next.js app on Aria

> A complete aria.yaml for a Next.js app with Postgres: build-time variables, migrations, health check, domain, and the commands to deploy and check it.

Source: https://getaria.cloud/docs/deploy-nextjs (updated 1 November 2026)

To deploy a Next.js app on Aria, describe it as one `web` service in `aria.yaml`, add a Postgres database, wire `DATABASE_URL` with `from: main.url`, and mark any `NEXT_PUBLIC_` variable your build reads. Aria then builds the app, runs your migration command, starts it and checks that it answers. Next.js runs as an ordinary long-running Node server on your own VPS.

## What does the stack file look like?

```yaml
version: 1
name: my-next-app
services:
  web:
    type: web
    framework: nextjs
    install: npm ci
    build: npm run build
    start: npm start
    port: 3000
    healthcheck: /api/health
    migrate: npx prisma migrate deploy
    domain: "@"
databases:
  main:
    type: postgres
env:
  DATABASE_URL:
    services: [web]
    from: main.url
  AUTH_SECRET:
    services: [web]
    generate: secret
  NEXT_PUBLIC_SITE_NAME:
    services: [web]
    value: My app
    build: true
  STRIPE_SECRET_KEY:
    services: [web]
    required: true
    secret: true
    description: Stripe secret key, from the Stripe dashboard
resources:
  users: 200
```

Swap `npx prisma migrate deploy` for your own migration command, or remove `migrate` if your app creates tables another way. Every field is explained in the [aria.yaml reference](https://getaria.cloud/docs/aria-yaml).

## What does each part do?

- `framework: nextjs` is a hint for sizing. A Next.js build needs more memory than a small API, and Aria counts that when it suggests a plan.
- `install`, `build` and `start` run when there is no Dockerfile. Each is one line.
- `port: 3000` is the port Aria routes to. Aria sets `PORT` to it, and `next start` reads it and listens on all interfaces by default.
- `healthcheck` is a path your app answers quickly with a 2xx and no login. Create a route at `/api/health` that returns "ok", or use `/` if your home page is public.
- `migrate` runs once per deploy, in the newly built image, before the new version takes traffic. If it fails, the old version keeps serving.
- `domain: "@"` asks for your main domain when you connect one. See [Domains](https://getaria.cloud/docs/domains).
- `NEXT_PUBLIC_SITE_NAME` is read while the app builds, so it needs `build: true`. Names that start with `NEXT_PUBLIC_` are passed at build time automatically, and anyone who visits the site can read them. Never wire a secret into one.
- `STRIPE_SECRET_KEY` is `required` and `secret`. The service waits until you enter it with `aria env add STRIPE_SECRET_KEY`. See [Environment variables](https://getaria.cloud/docs/environment-variables).

## Can I use a Dockerfile instead?

Yes. Set `dockerfile` and Aria ignores `install`, `build` and `start`. Declare every variable the build reads with `ARG`:

```dockerfile
FROM node:22-alpine
WORKDIR /app
ARG NEXT_PUBLIC_SITE_NAME
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
ENV NODE_ENV=production
CMD ["npm", "start"]
```

```yaml
services:
  web:
    type: web
    dockerfile: Dockerfile
    port: 3000
    healthcheck: /api/health
```

## What if my app is in a monorepo?

Set `path` to the app's folder and `context` to the root of the workspace, so the build can see shared packages. The commands then run from that root:

```yaml
services:
  web:
    type: web
    path: apps/web
    context: .
    build: pnpm install --frozen-lockfile && pnpm --filter web... build
    start: pnpm --filter web start
    port: 3000
```

A push rebuilds a service only when files under its `path` change, plus the root files such as the lockfile when `context` is `.`. Code in a shared package outside every service's `path` rebuilds nothing, so run `aria deploy` after changing it. A monorepo build needs more memory, and Aria counts that too.

## Are there more examples?

Two whole stacks are written out in the [aria.yaml reference](https://getaria.cloud/docs/aria-yaml): a three-service stack with Postgres and Redis, and a web app on self-hosted Supabase, which passes `NEXT_PUBLIC_` variables into a Dockerfile build.

## How do I deploy it?

[Aria AI](https://getaria.cloud/docs/aria-ai) can write the file for you during setup. Otherwise commit `aria.yaml` and push. Once the app is live, a push redeploys the services that the commit touched. For the first launch, or to apply a changed `aria.yaml` that you did not push:

```bash
aria deploy --wait
aria check
aria open
```

`aria deploy web` deploys one service. If something fails, `aria logs web --lines 200` shows its recent output.

## Which plan do I need?

Aria estimates your stack's memory from `aria.yaml` and recommends the smallest plan that fits, with room for builds. For the file above, with Postgres, the estimate is about 4.0 GB, which just fits the Starter plan. Add Redis and it is about 4.2 GB, which needs Developer. The plan table is on the [billing page](https://getaria.cloud/docs/billing), and prices are on [pricing](https://getaria.cloud/pricing).

For the reasoning behind running Next.js on your own server, read [Deploy Next.js on your own server](https://getaria.cloud/blog/deploy-nextjs-on-your-own-server). If your app came from an AI builder, read [Deploy a vibe-coded app](https://getaria.cloud/deploy-vibe-coded-apps).

Next: [Self-hosted Supabase on Aria](https://getaria.cloud/docs/supabase)
