> ## Documentation Index
> Fetch the complete documentation index at: https://docs.avocadostudio.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy to Netlify

> Deploy a site that uses Avocado Studio, and the editor, to Netlify, with the orchestrator on a host that keeps its state.

This page is for the self-hoster putting a site that uses Avocado Studio on
Netlify. The constraints are the same as on Vercel: the site and the editor
deploy well, and the orchestrator's state needs a persistent disk.

## Architecture overview

| Component | Where | Notes |
| - | - | - |
| **Your site** | Netlify | Next.js |
| **The editor** (`apps/editor`) | Netlify | Static Vite build |
| **The orchestrator** | A host with a persistent disk | The [Docker image](/operations/docker-deployment), or [from source](/operations/vercel-deployment#orchestrator-from-source) |

<Note>
  The orchestrator keeps drafts, undo history and the version log in a SQLite
  file, so it needs a persistent disk and a single instance. A Netlify Function
  has neither. That includes library mode mounted inside a site deployed to
  Netlify: it answers requests, but its drafts do not survive. See
  [state and backups](/operations/state-and-backups).
</Note>

## This repository's demo site

These settings deploy `apps/site` from this repository. For your own site, use
your usual Netlify settings and the variables below.

1. Connect your repository to Netlify.
2. Configure build settings:
   * **Base directory:** `apps/site`
   * **Build command:** `cd ../.. && pnpm install --frozen-lockfile && pnpm --filter @ai-site-editor/site build`
   * **Publish directory:** `apps/site/.next`
3. Install the Next.js plugin: add `@netlify/plugin-nextjs` via the Netlify UI or `netlify.toml`.

```toml theme={null}
[build]
  base = "apps/site"
  command = "cd ../.. && pnpm install --frozen-lockfile && pnpm --filter @ai-site-editor/site build"
  publish = ".next"

[[plugins]]
  package = "@netlify/plugin-nextjs"
```

| Variable | Value | Required |
| - | - | - |
| `NODE_VERSION` | `22` | Yes |
| `ORCHESTRATOR_URL` | *(leave unset to serve the committed published content)* | No |
| `SITE_PUBLISH_SITE_ID` | your site id | **Yes** whenever `ORCHESTRATOR_URL` is set, or the build fails |
| `SITE_PUBLISH_SESSION` | publish session name | No, defaults to `dev` |

With `ORCHESTRATOR_URL` unset, the demo site renders from
`lib/published-content.json` and needs no orchestrator.

## The editor

Create a separate Netlify site for `apps/editor`:

* **Base directory:** `apps/editor`
* **Build command:** `cd ../.. && pnpm install --frozen-lockfile && pnpm --filter @ai-site-editor/editor build`
* **Publish directory:** `apps/editor/dist`

The editor is a single-page app, so add a rewrite of every path to `/index.html`.

| Variable | Value |
| - | - |
| `VITE_SITE_ORIGIN` | `https://<your-site>.example.com` |
| `VITE_ORCHESTRATOR_URL` | `https://<your-orchestrator>.example.com` |

<Warning>
  **Do not set `VITE_SITE_DRAFT_SECRET` or `VITE_PUBLISH_TOKEN` on a public
  editor.** Vite compiles `VITE_*` values into JavaScript anyone can download.
  Give the orchestrator `DRAFT_MODE_SECRET`, `PUBLISH_TOKEN` and
  `ACCESS_PASSWORD_HASH` instead. The editor fetches the first two from
  `GET /editor/credentials` after sign-in.
</Warning>

## Site environment variables, with editing

| Variable | Value |
| - | - |
| `ORCHESTRATOR_URL` | `https://<your-orchestrator>.example.com` |
| `DRAFT_MODE_SECRET` | Shared secret. Set the same value on the orchestrator. |
| `PUBLISH_TOKEN` | The publish secret, passed to `createEditorApiHandler` as `publishSecret`. Set the same value on the orchestrator, which sends it as `x-publish-token`. |
| `NEXT_PUBLIC_EDITOR_ORIGIN` | `https://<your-editor>.example.com`, for the preview bridge |
| `EDITOR_CORS_ORIGINS` | The same editor origin, for CORS on `/api/editor/*` |

`PUBLISH_TOKEN` is what makes publishing work at all here: the site's
`POST /api/editor/publish` refuses every request with a 401 under
`NODE_ENV=production` when no secret is configured, and a production build is
what Netlify serves. The same deploy also has a read-only filesystem, so a
publish handler that rewrites a JSON file in the repository succeeds locally and
fails here. Publish to a CMS, a database, or a commit that triggers a rebuild.

## Other platforms

The orchestrator half of any other platform is covered by
[Docker deployment](/operations/docker-deployment): it runs anywhere that
supports one long-lived process with a persistent volume.

## Troubleshooting

See [production troubleshooting](/operations/vercel-deployment#production-troubleshooting).
The same draft-secret, preview-bridge and CORS issues apply on every host.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.