# Deployment

Deploy a Nextly application to Vercel (recommended) or self-host on Node.js. Production checklist, environment variables, and database migrations.

A Nextly application is a standard Next.js app, so anywhere Next.js runs is fair game. This guide covers the two paths Nextly is built around:

* **Vercel** (recommended) -- the default target. Zero-config Next.js, first-class Vercel Blob storage, build-step migrations.
* **Self-host** -- Node.js 20.19+, 22.12+ or 24+ behind a process manager and reverse proxy. Linked out for the operator-side details rather than reproduced here.

Whichever you choose, run database migrations on a CI/CD step (or your laptop) **before** the new app code takes traffic. The deployed app never touches schema -- see [Production migrations](https://nextlyhq.com/docs/guides/production-migrations.md) for why.

## Production checklist (the short list)

Before you ship, set the following:

| Variable                 | Required               | Purpose                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NODE_ENV`               | yes                    | Set to `production`.                                                                                                                                                                                                                                                                                                                                                         |
| `DB_DIALECT`             | yes                    | `postgresql`, `mysql`, or `sqlite`. PostgreSQL recommended.                                                                                                                                                                                                                                                                                                                  |
| `DATABASE_URL`           | yes (Postgres / MySQL) | Database connection string.                                                                                                                                                                                                                                                                                                                                                  |
| `NEXTLY_SECRET`          | yes                    | JWT signing secret, **at least 32 characters**. Generate with `openssl rand -base64 32`.                                                                                                                                                                                                                                                                                     |
| `NEXT_PUBLIC_APP_URL`    | yes                    | The public URL of your deployment (used in client code and email links).                                                                                                                                                                                                                                                                                                     |
| `NEXTLY_ALLOWED_ORIGINS` | self-hosted            | Comma-separated additional CSRF origins: the origin browsers use whenever it differs from the address a self-hosted server listens on -- behind a proxy, by IP or hostname, or in a container (the origin check otherwise sees only that address, and refuses sign-in and plugin-route writes with `CSRF_FAILED`), plus any other origin such as a separate frontend domain. |

Plus storage env vars if you're using cloud storage (see [Media & Storage](https://nextlyhq.com/docs/guides/media-storage.md)) and email env vars if you're using a real provider (see [Email](https://nextlyhq.com/docs/guides/email.md)).

> **Note:** Boot-time validation refuses to start in production unless `NEXTLY_SECRET` is at least 32 characters and `NEXT_PUBLIC_APP_URL` is set.

## Deploy to Vercel

Vercel is the recommended platform for Nextly. Next.js detection is automatic, the Vercel Blob adapter is the easiest storage option, and the build step doubles as a migration runner.

### 1. Push to GitHub and import

1. Push the project to GitHub, GitLab, or Bitbucket.
2. Open the [Vercel Dashboard](https://vercel.com/new), import the repository.
3. Vercel detects Next.js and configures the build pipeline. Override only if you've customised the build script (see step 3 below).

### 2. Configure environment variables

In **Project Settings -> Environment Variables**, add everything from the production checklist above, plus storage and email variables you intend to use.

Set them for **Production** and **Preview** environments separately if you want preview deployments to talk to a staging database.

### 3. Build command

A scaffolded Nextly project ships this `build` script in `package.json`:

```json
{
  "scripts": {
    "build": "nextly migrate && next build"
  }
}
```

The `nextly migrate` step runs against the database referenced by `DATABASE_URL` during the Vercel build, so pending migrations are applied as part of every deploy. Migrations are forward-only -- to undo, write a new corrective migration. See [Production migrations](https://nextlyhq.com/docs/guides/production-migrations.md) for the deeper trade-offs (build-time vs. CI-step migrations) and recovery patterns.

If you'd rather run migrations in a separate CI job before the Vercel deploy promotes (recommended once you have multiple developers / preview deploys), wire it into a GitHub Actions workflow as described in the migrations guide and remove `nextly migrate` from `build`.

### 4. Storage on Vercel

The default zero-config local-disk adapter does **not** work on Vercel -- serverless function instances are ephemeral, so `./public/uploads/` is wiped on cold start. Pick one of:

* **Vercel Blob** (easiest) -- install `@nextlyhq/storage-vercel-blob`, set `BLOB_READ_WRITE_TOKEN`. The auto-detection picks it up:

  ```typescript
  // nextly.config.ts
  import { defineConfig } from "nextly/config";
  import { getStorageFromEnv } from "nextly/storage";

  export default defineConfig({
    storage: await getStorageFromEnv(),
  });
  ```

* **S3 / R2 / DigitalOcean Spaces / etc.** -- install `@nextlyhq/storage-s3`, set `S3_BUCKET` + `S3_REGION` + `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` (and `S3_ENDPOINT` + `S3_FORCE_PATH_STYLE` for non-AWS providers).

Vercel's serverless functions also have a 4.5MB request-body limit. Enable client-side direct uploads on storage collections that need to accept larger files:

```typescript
// nextly.config.ts
import { defineConfig } from "nextly/config";
import { vercelBlobStorage } from "@nextlyhq/storage-vercel-blob";

export default defineConfig({
  storage: [
    vercelBlobStorage({
      token: process.env.BLOB_READ_WRITE_TOKEN,
      collections: {
        media: {
          clientUploads: true,
        },
      },
    }),
  ],
});
```

See [Media & Storage](https://nextlyhq.com/docs/guides/media-storage.md) for the full storage reference.

### 5. Database

Use a managed PostgreSQL service that's reachable from Vercel's IP ranges:

* [Vercel Postgres](https://vercel.com/storage/postgres)
* [Neon](https://neon.tech)
* [Supabase](https://supabase.com)
* [PlanetScale](https://planetscale.com) (MySQL)

SQLite is **not** suitable for Vercel deployments -- the file would be wiped on every cold start. See [Database](https://nextlyhq.com/docs/database.md) for adapter-specific guidance.

## Self-host with Node.js

Run Nextly on any host that supports Node.js 20.19+, 22.12+ or 24+. The deployment shape is intentionally boring: build the app, run it under a process manager, put a reverse proxy in front for SSL.

### Requirements

* **Node.js 20.19+, 22.12+ or 24+** (matches the `serverExternalPackages` and `sharp` ABI requirements).
* **A process manager** -- pm2, systemd, Docker, or similar. Keep the Node process supervised so crashes auto-restart.
* **A reverse proxy** -- nginx or Caddy in front for TLS termination, HTTP/2, and access logging.

### Build and start

```bash
pnpm install --frozen-lockfile
pnpm build
pnpm start
```

The scaffolded `build` script runs `nextly migrate && next build`, and `pnpm start` is `next start`. If you'd rather run migrations as a separate step (recommended for production with zero-downtime deploys), drop `nextly migrate` from `build` and run it via your CI pipeline before promoting the new release.

### Environment variables

Configure the same variables listed in the production checklist above. On most hosts that means a `.env` file outside the repo (loaded by your process manager) or a secrets manager.

### Process manager and reverse proxy

Use a process manager (pm2, systemd, Docker, etc.) and a reverse proxy (nginx, Caddy, etc.) -- consult their documentation for setup specific to your distribution. The Nextly process is a plain Next.js server; nothing about it requires special configuration beyond ensuring the proxy forwards `Host` and `X-Forwarded-Proto` so callbacks resolve correctly.

If your reverse proxy is on a separate IP and you rely on rate-limiting / refresh-token IP binding, set `security.trustProxy: true` in `nextly.config.ts` and populate the `TRUSTED_PROXY_IPS` env var with the proxy's CIDR ranges. Otherwise Nextly ignores `X-Forwarded-For` and treats every request as coming from a single bucket, which is the safe default for direct-internet deployments.

### Migrations on deploy

For self-hosted deploys, the same forward-only model applies. Run `nextly migrate` against the production database **before** the new app binary takes traffic. See [Production migrations](https://nextlyhq.com/docs/guides/production-migrations.md) for GitHub Actions, Railway, Render, Fly.io, and AWS examples.

## Final checklist

Before going live, verify:

* [ ] `NODE_ENV=production`.
* [ ] `NEXTLY_SECRET` is at least 32 characters (generated with `openssl rand -base64 32`).
* [ ] `DATABASE_URL` points to a production database (not localhost / SQLite).
* [ ] `NEXT_PUBLIC_APP_URL` is the public URL of your deployment.
* [ ] `NEXTLY_ALLOWED_ORIGINS` lists the origin browsers use whenever it differs from the address the self-hosted server listens on (behind a proxy, by IP or hostname, or in a container), and any additional CSRF origins (separate frontend domain, mobile bundle, etc.).
* [ ] Storage adapter is configured for non-ephemeral filesystems (no local disk on serverless).
* [ ] An email provider is configured (Resend, SMTP, or SendLayer) -- without one, password-reset and verification tokens are returned in the API response as a development fallback.
* [ ] All pending migrations have been applied (`nextly migrate:status` reports zero pending and zero unfinished). *Needs the release after 0.0.2-alpha.66.*
* [ ] The reverse proxy / load balancer terminates TLS and forwards real client headers.
* [ ] Database backups are configured (your hosting provider's snapshots, point-in-time recovery, etc.).
* [ ] Monitoring / error reporting is wired up (Sentry, Datadog, etc.).
* [ ] `.env*` files are not committed to version control.

## Next steps

* [Production migrations](https://nextlyhq.com/docs/guides/production-migrations.md) -- forward-only migration model, GitHub Actions and other CI examples.
* [Authentication](https://nextlyhq.com/docs/guides/authentication.md) -- session, RBAC, and API key configuration.
* [Media & Storage](https://nextlyhq.com/docs/guides/media-storage.md) -- adapter-specific configuration for S3, Vercel Blob, Uploadthing, local disk.
* [Email](https://nextlyhq.com/docs/guides/email.md) -- provider setup and local Mailpit testing.
* [Environment Variables](https://nextlyhq.com/docs/configuration/environment.md) -- full environment variable reference.
* [Database](https://nextlyhq.com/docs/database.md) -- adapter setup and connection options.
