Skip to main content
The canonical Pylon production stack is frontend on Vercel, backend on Pylon Cloud (Fly Machines under the hood). This guide takes you from a working npm create @pylonsync/pylon app to a live production URL. If you haven’t scaffolded yet:
npm create @pylonsync/pylon@latest my-app -- --template todo --platforms web
cd my-app
bun install && bun run dev
That gives you apps/api (Pylon backend) and apps/web (Next.js frontend) ready to deploy.

1. Deploy the backend to Pylon Cloud

Sign in at cloud.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:
NameValueEnvironments
PYLON_TARGEThttps://pylon-<your-slug>.fly.devProduction, 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.
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 below.

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

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). 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_SAMESITE=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

Custom domains

Wire DNS for your Pylon Cloud project.

Cookie & sessions

Full cookie config reference.

Next.js SDK

All @pylonsync/next APIs.

Pylon Cloud

Project management, scaling, custom domains.