# Deploy a Next.js app

> From a Next.js directory to a URL with one command, then secrets, logs, releases and rollbacks. The same path works for every framework Rytiv detects.

`ry deploy` reads your project, uploads it, builds it on Rytiv's build machines and deploys the result behind `https://<app>.rytiv.app`. Next.js is detected from `next` in your dependencies; nothing in the project has to change.

## Before you start

- A Rytiv account and the CLI: `curl -fsSL https://get.rytiv.com/install.sh | sh`, then `ry login`. See [Authentication](https://docs.rytiv.com/authentication.md).
- A Next.js project with a `package-lock.json` (the builder runs `npm install --include=dev`; a pnpm, yarn or bun lockfile without a `package-lock.json` is ignored, with a note).

## 1. Deploy

1. From the project directory, deploy to an app named `web`:

   ```bash
   ry deploy web --source .
   ```

   The app is created if it does not exist. The CLI prints what it detected (framework, build and start command, port), uploads the directory, shows the build's place in line and its progress, then rolls the deploy.

2. Open the URL the last line prints: `https://web.rytiv.app`.

3. Save the settings so the next deploy needs no flags:

   ```bash
   ry config init web
   ```

   This writes a `rytiv.json` next to your code. `ry deploy` reads it from the current directory or any directory above it; a flag on the command line always wins over the file.

Or run `ry` on its own in the directory: it signs you in if needed, shows what it detected, asks for an app name and deploys. `ry new next my-site` scaffolds a new Next.js project with a `rytiv.json`.

## What is detected

| What | Value for Next.js |
| --- | --- |
| Build command | `npm run build` |
| Start command | `npx next start -H 0.0.0.0 -p $PORT` |
| Port | `3000`, unless `.env.example` sets `PORT` or the start script passes `-p` |
| Build machine | 8192 MB, 4 vCPU (the larger of the two build sizes; Next.js and Dockerfile builds get it) |
| Suggested machine | 1024 MB, 1 vCPU for a new app |
| Served at the edge | `.next/static` at `/_next/static`, and `public/` at `/` |

A `Dockerfile` or `Containerfile` at the root of the directory wins over the detection. `next.config.js` (also `.mjs`, `.cjs`, `.ts`) is read as text, never executed; `output: "export"` makes the app a static site served from `out/` without a machine.

What is uploaded: the directory without `node_modules`, `.git`, `.next`, `.svelte-kit`, `.nuxt`, `.output`, `.astro`, `.env.local` and `.env.*.local`. Your `.gitignore` is not applied; a `.env` file is uploaded, and the CLI warns about `.env*` files that git ignores.

> **The port:** The platform exports `PORT=3000` (with `HOST=0.0.0.0` and `NODE_ENV=production`) into the machine; `--port` tells Rytiv where to route and probe, it does not change `PORT`. If your app must listen on another port, set `PORT` yourself with `ry deploy -e PORT=8080 --port 8080`, or keep 3000.

## 2. Environment and secrets

**CLI**

```bash
ry secrets set web DATABASE_PASSWORD=example-value
ry secrets set web NEXT_PUBLIC_SITE_URL=https://www.example.com --build
ry deploy web -e LOG_LEVEL=info
```

**curl**

```bash
curl -X PUT "https://api.rytiv.app/v1/apps/web/secrets" \
  -H "Authorization: Bearer $RYTIV_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"secrets": {"DATABASE_PASSWORD": "example-value"}, "build": {"DATABASE_PASSWORD": false}}'
```

**TypeScript**

```ts
await fetch("https://api.rytiv.app/v1/apps/web/secrets", {
  method: "PUT",
  headers: { Authorization: `Bearer ${process.env.RYTIV_TOKEN}`, "Content-Type": "application/json" },
  body: JSON.stringify({ secrets: { DATABASE_PASSWORD: "example-value" } }),
});
```

**Python**

```python
import os, requests

requests.put(
    "https://api.rytiv.app/v1/apps/web/secrets",
    headers={"Authorization": f"Bearer {os.environ['RYTIV_TOKEN']}"},
    json={"secrets": {"DATABASE_PASSWORD": "example-value"}},
)
```

- **Secrets** are stored encrypted and never readable back. They reach the app at its next deploy; `ry secrets set --now` rolls the app right away.
- **Runtime or build.** Every secret is available to the running app. Only secrets marked for the build are visible to `next build`: a new secret whose name starts with `NEXT_PUBLIC_` (or `VITE_`, `PUBLIC_`, `NUXT_PUBLIC_`, `GATSBY_`, `REACT_APP_`) is marked by default, anything else needs `--build`. `--no-build` takes the mark away. In the API this is the `build` map of `PUT /v1/apps/:name/secrets`.
- **Plain variables** (`-e KEY=VALUE`) are stored in the clear and shown in `ry machines`; use them for settings, not credentials.

A [Postgres database](https://docs.rytiv.com/postgres.md) attached with `ry attach appdb --to web` gives the app its `DATABASE_URL` as a managed secret.

## 3. Watch it run

```bash
ry logs web -f
ry status
ry machines web
```

`ry logs` shows what the app's machines write to their console; `-f` follows. A machine is `running` when it answers on its port.

## 4. Health checks and sizes

```bash
ry deploy web --health-path /api/health --health-timeout 90
ry deploy web --memory 2048 --vcpus 2
```

With `--health-path`, a new machine gets traffic only after that path answered `2xx` or `3xx`; without it, the port alone decides. A size you do not name keeps the app's current one. See [`ry deploy`](https://docs.rytiv.com/reference/cli/deploy.md) for every flag.

## 5. Releases and rollback

```bash
ry deploys web
ry releases web
ry rollback web
```

Every deploy that goes live is a release. `ry rollback web` deploys the previous release's image again, without a rebuild and with the app's current settings; `ry rollback web 3` names a version. Secrets, volumes and databases stay as they are.

After a deploy switches traffic, Rytiv watches the app's error rate for five minutes against the rate before the switch. If at least 20 requests came in and the share of `5xx` answers is clearly worse than before (at least 5 percent, and more than three times the old rate plus two points), the deploy is rolled back on its own. The verdict is in the deploy's `watch` field (`GET /v1/deploys/:id`): `healthy`, `unproven` (not enough traffic to judge), `rolled-back`, and a few more. Databases are never rolled back automatically.

## 6. Deploy on push

```bash
ry git connect web --repo acme/web --branch main
```

This installs Rytiv's GitHub app through the browser and links the app to the repository. Every push to the branch builds and deploys through the same path as `ry deploy`; `ry git status web` shows the last push. Previews (`ry preview create web --branch feature/login`) are apps of their own per branch, protected by a link, closed when the branch or pull request closes or after 14 idle days, at most 10 per app.

## Next

- [Custom domains and TLS](https://docs.rytiv.com/guides/custom-domains.md): put `www.example.com` on the app.
- [Cron and jobs](https://docs.rytiv.com/guides/cron-and-jobs.md): call a path on a schedule, run a migration as a job.
- [Apps](https://docs.rytiv.com/apps.md): everything an app can do, and the deploy request field by field.
