Picking a host
Which host to run your app on, and the two rules that hold whichever one you pick.
Nothing about the app is Vercel-specific except vercel.json and api/chatfuel.ts; delete both
and you have a working Node app. So the host you pick decides who manages TLS, whether you can
serve from a sub-path and how long a WebSocket stays open — not what the code is.
Picking a host
| Vercel | Your own server | Docker | |
|---|---|---|---|
| Command | npm run deploy | npm run build && npm start | docker build + docker run |
| Needs an account | Vercel | none | none |
| TLS, domain | managed | yours | yours |
Sub-path (/app/) | no, root only | yes | yes |
| WebSocket lifetime | capped by the function duration (300 s on Hobby); the client reconnects | unbounded | unbounded |
| Where the env lives | the Vercel project (.env is never uploaded) | your process | --env-file and build args |
All three run the same proxy source, so the auth gate, the bot fence and the error codes are identical between them.
Every host has to answer an unknown path with index.html
The router is path-based, so /deals/board is a real address. A host that 404s it produces a
failure that looks like a routing bug and is not one: a reload of that URL fails while clicking
through to the same page works fine.
- Vercel — the last rewrite in
vercel.json. - nginx —
try_files $uri /index.html; - Static hosts — their SPA-fallback, or "rewrite everything to
index.html". - The bundled Node server does it already.
The rule that catches everyone once
VITE_* variables are compiled into the browser bundle at build time. Everything else is read
by the server at runtime. A deployment where those two disagree does not fail at the mismatch;
it fails later, at the gate:
- client built with Supabase, server started without it → the sign-in screen renders and every
request comes back
ProxyAuthMisconfiguredorAuthSessionRequired; - server gated, client built without it → nobody is ever asked to sign in, and the server rejects everything.
Neither is fixable at runtime. Rebuild with the same values the server is running with.
The server prints its mode on startup, and /healthz answers
{"ok":true,"auth":"on|off|misconfigured"}. Read that before any other symptom.
Deploying without the wizard
npm run deploy is a convenience, not a dependency: it is scripts/deploy-vercel.mjs inside your
own project, driving the Vercel CLI. Any pipeline that builds the client, builds the server and
sets the environment produces the same result, and any CI that can run npm run build can deploy
this app.
Vercel
One command: links the project, pushes the environment, deploys, verifies.
Your own server
npm run build && npm start, and what to put in front of it.
Docker
The shipped Dockerfile, and the build-arg split that leaks secrets.
Environment variables
Every variable the app reads, which side reads it, and what it is.