When you want your server node to live somewhere other than hardware you own. Spec §10.2.
V3 ships three cloud templates today:
| Target | Cloud env chats? | Best for |
|---|---|---|
| Fly.io | ✅ (Docker-in-Docker Machine) | Users with no home server who want the full mesh + Cloud env |
| Railway | Users who want the simplest setup and don't care about Cloud env | |
| Cloudflare Pages | ❌ (static SPA only) | Hosting only the cloud-mode web bundle — you still need a server node somewhere |
For self-host on your own machine, see deploy-self.md. For a fresh rented VPS, see deploy-vps.md.
- A Google Cloud OAuth 2.0 Web application client ID + secret.
Authorize your target's public URL as a redirect URI under
/auth/google/callback. - Optional but recommended: a GitHub OAuth App (client ID + secret). Needed for Cloud env chats to commit/push/PR.
- The email address you want V3 to accept sign-ins from — this lands
in
[auth].authorized_emails. - A domain name you control if you want a pretty URL. All three
targets give you a default
*.fly.dev/*.up.railway.app/*.pages.devyou can start with.
The full-fat option. Fly Machines run Docker-in-Docker so Cloud env chats work without external plumbing.
Full walkthrough lives in deploy/flyio/README.md. Short version:
cp deploy/flyio/fly.toml fly.toml
fly launch --no-deploy --name v3-<your-handle> --region iad
# Set secrets
fly secrets set \
V3CODE_GOOGLE_CLIENT_ID=<...> \
V3CODE_GOOGLE_CLIENT_SECRET=<...> \
V3CODE_GITHUB_CLIENT_ID=<...> \
V3CODE_GITHUB_CLIENT_SECRET=<...> \
V3CODE_AUTHORIZED_EMAILS=you@example.com \
V3CODE_TOKEN_ENCRYPTION_KEY=$(openssl rand -base64 32)
# Attach Postgres
fly postgres create --name v3-<your-handle>-db
fly postgres attach v3-<your-handle>-db
fly deployThe template sizes the Machine at 8 GB / 4 vCPU so Cloud env chats
(spec §7.2 caps a container at 4 GB) have room alongside the server +
Postgres. Shrink it via fly scale memory 2048 if you disable Cloud
env.
- Public URL:
https://v3-<your-handle>.fly.dev(or custom domain viafly certs). - Postgres 16 attached.
- Docker-in-Docker Machine for Cloud env chats.
- WebSocket upgrades handled automatically by the Fly proxy.
Fly's Machine pricing + Postgres pricing adds up to roughly $15–30/mo
for a single-user deployment that sees 1–2 concurrent Cloud env chats.
Lower if you scale to zero when idle (see auto_stop_machines in
fly.toml).
Lighter-weight. No Docker-in-Docker. Cloud env chats won't work without a separate Docker host, which V3 does not configure for you on Railway.
Full walkthrough in deploy/railway/README.md. Short version:
railway login
railway link
railway add --database postgres
railway variables set \
V3CODE_MODE=server-node \
V3CODE_GOOGLE_CLIENT_ID=<...> \
V3CODE_GOOGLE_CLIENT_SECRET=<...> \
V3CODE_AUTHORIZED_EMAILS=you@example.com \
V3CODE_TOKEN_ENCRYPTION_KEY=$(openssl rand -base64 32)
railway upThe DATABASE_URL that Railway injects is read automatically.
- Cloud env chats will fall through to "not available" because there's no Docker socket reachable from a Railway container.
- Good fit if you only ever host chats on physical devices (desktop, laptop, phone) and use the server node purely as a sync hub.
Pages is not a server-node deploy target. It hosts the "cloud-mode" web bundle — the React SPA that talks to a server node somewhere else — behind a Cloudflare-edge CDN.
Useful if:
- Your server node is on a Tailnet-only URL and you want a public web entry point without re-exposing the server.
- You want to run
app.example.comon Pages for the UI andv3.example.comon Fly.io/VPS for the server, with separate TLS terminations.
Full walkthrough in deploy/cloudflare-pages/README.md. Short version:
bun install
bun run build:web-cloud
cd deploy/cloudflare-pages
wrangler pages deploy ../../apps/web/dist-cloud \
--project-name v3-cloud-app \
--branch mainConfigure the Pages project's environment variables with the URL of
your V3 server node (VITE_V3_SERVER_URL). The SPA makes all calls to
that origin over HTTPS + WSS.
- Cloudflare Workers + D1 + R2 + Containers as a full server-node target. The Containers public beta landed in 2026 but we haven't shipped the template — tracked in TODO.md / open issues. For now Cloudflare Pages handles the static web path only.
- Point a domain at the public URL (or use the default host).
- Register redirect URIs with Google OAuth.
- Sign in on a client device — the first one auto-approves (spec §3.3). Subsequent devices need an approval click from a device that's already online.
- Write the server URL to Drive App Data — the sign-in flow does this automatically.
Monitor the server node with /admin and the troubleshooting guide at
troubleshooting.md.