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

# Deploying to Vercel

> Ship a Next.js + Pylon app to production on Vercel — env vars, cookie config, CORS checklist.

One supported production shape is a **Next.js frontend on Vercel** with the **Pylon backend on Pylon Cloud** (Fly Machines under the hood). This guide takes you from a scaffolded Next.js + Pylon monorepo to a live production URL. (If you don't need a separate Next.js app, Pylon's default scaffold serves server-rendered React and the API from one port — see [Deploy](/operations/deploy) — and you can skip Vercel entirely.)

If you haven't scaffolded yet, generate the Next.js + Pylon monorepo:

```bash theme={null}
pylon init my-app --frontend nextjs
cd my-app
bun install && bun dev
```

That gives you `apps/api` (Pylon backend) and `apps/web` (Next.js frontend) ready to deploy — `apps/web/next.config.js` already proxies `/api/*` to the backend on `:4321`.

## 1. Deploy the backend to Pylon Cloud

Sign in at [www.pylonsync.com](https://www.pylonsync.com), create an organization, and connect the GitHub repo. The setup wizard provisions a Fly Machine, points it at `apps/api/app.ts`, and gives you a default hostname:

```
https://pylon-<your-project-slug>.fly.dev
```

Push to the branch you configured (default: `main`) and the cloud auto-deploys.

You can also add a custom domain (`api.example.com`) in the project's **Domains** tab. Recommended once you go live — keeps the URL stable across project renames.

## 2. Deploy the frontend to Vercel

Create a Vercel project pointed at the same GitHub repo. In **Project Settings → Build & Development Settings**:

* **Framework Preset**: Next.js (auto-detected)
* **Root Directory**: `apps/web`
* **Install Command**: `bun install` (or your PM)
* **Build Command**: `next build`

Vercel will pick up `apps/web/next.config.js` automatically.

## 3. Set the backend URL

In **Project Settings → Environment Variables**, add:

| Name           | Value                               | Environments        |
| -------------- | ----------------------------------- | ------------------- |
| `PYLON_TARGET` | `https://pylon-<your-slug>.fly.dev` | Production, Preview |

The Next.js template's `next.config.js` rewrites `/api/*` to `${PYLON_TARGET}/api/*`, so the browser always talks same-origin to Vercel and Vercel forwards to your Pylon backend. **This is the recommended pattern** — it sidesteps CORS preflights and lets the session cookie ride natively.

For local dev (`bun run dev`), `PYLON_TARGET` defaults to `http://localhost:4321` so no `.env.local` is required when your backend is `pylon dev` on the same machine.

<Tip>
  If you'd rather skip the rewrite and have the browser talk to your Pylon backend directly, set `Domain=.example.com` on the session cookie + `allowed-origins` in your Pylon manifest to your Vercel hostname. See the [cookie checklist](#cookie-checklist) below.
</Tip>

## 4. Configure CORS (only if you skipped the rewrite)

If your Next.js rewrite proxies `/api/*`, **you don't need CORS** — the browser only sees the Vercel origin.

If your frontend talks to Pylon Cloud directly (`fetch("https://api.example.com/api/fn/...")` from the browser), open your project's **Settings → CORS** tab on Pylon Cloud and add your Vercel deployment URLs:

```
https://my-app.vercel.app
https://my-app-git-main.vercel.app
https://*.my-app.vercel.app
```

Vercel mints a unique preview URL per deployment, so use the wildcard pattern for the preview environment.

## 5. Push and verify

```bash theme={null}
git push origin main
```

* **Vercel build**: should take \~30s. Watch the deploy log for the build step.
* **Pylon Cloud deploy**: parallel; check the project's **Deployments** tab.

Once both finish, open your Vercel URL. You should see your app, and the Network tab should show `/api/*` requests succeeding (status 200, `Set-Cookie` header on auth endpoints).

<h2 id="cookie-checklist">
  Cookie checklist
</h2>

If session cookies aren't sticking, work through this list:

* **Cookie name matches.** Pylon emits `${app_name}_session`. Your `createPylonServer({ cookieName })` and `createPylonProxy({ cookieName })` must pass the same name. Mismatch = silent 401s.
* **Same-site = Lax.** Pylon defaults to `SameSite=Lax`, which works with Next.js's same-origin pattern. If you must use cross-site (skipping the rewrite), set `PYLON_COOKIE_SAME_SITE=None` and `PYLON_COOKIE_SECURE=true`.
* **Domain matches.** If frontend is `app.example.com` and backend is `api.example.com`, set `PYLON_COOKIE_DOMAIN=.example.com` so the cookie applies to both subdomains. Don't set this if you're using the rewrite pattern — host-only cookies are tighter.
* **iOS Chrome / Safari.** iOS browsers reject domain-scoped cookies when a host-only cookie of the same name is present. Pylon v0.3.140+ handles this by pairing every domain-scoped set with a host-only `Max-Age=0` clear. Make sure your Pylon backend is on a recent framework version.

## Custom domain end-to-end

```
api.example.com → Pylon Cloud (custom domain on the project)
app.example.com → Vercel (custom domain on the Vercel project)
```

With this layout you set:

* Pylon Cloud project domains: `api.example.com`
* Vercel project domains: `app.example.com`
* Vercel env var: `PYLON_TARGET=https://api.example.com`
* Your `next.config.js` rewrite continues to forward `/api/*` to `${PYLON_TARGET}/api/*`

No CORS config needed; the browser only ever talks to `app.example.com`.

## Troubleshooting

**Build fails: "Module not found: @pylonsync/next".** You're probably building from the repo root instead of `apps/web`. Set Vercel's **Root Directory** to `apps/web`.

**`Too many requests. Limit: 100 per 60s` from the dashboard.** Pylon defaults to 100 requests/min per authenticated user. Bump it via `PYLON_RATE_LIMIT_MAX_AUTHED` (default 1000 since v0.3.148) on your Pylon Cloud project.

**`PYLON_BACKEND_UNREACHABLE` errors in Next.js logs.** Your Pylon Cloud machine is stopped (autostop after idle) or restarting. The server helpers time out after 5s; an `error.tsx` boundary makes this graceful. Restart the machine from the project's **Overview** tab.

**Live queries don't update across tabs.** The sync engine WebSocket may be misrouted. Check that `PYLON_TARGET` resolves and that your browser's WebSocket connection (DevTools → Network → WS) connects to `wss://<your-target>/api/sync` successfully.

## Next steps

<CardGroup cols={2}>
  <Card title="Custom domains" icon="globe" href="/operations/deploy">
    Wire DNS for your Pylon Cloud project.
  </Card>

  <Card title="Cookie & sessions" icon="cookie" href="/auth/sessions">
    Full cookie config reference.
  </Card>

  <Card title="Next.js SDK" icon="react" href="/clients/next">
    All `@pylonsync/next` APIs.
  </Card>

  <Card title="Pylon Cloud" icon="cloud" href="/cloud">
    Project management, scaling, custom domains.
  </Card>
</CardGroup>
