Deploy Your Chatfuel Wizard App: Vercel, Node.js or Docker
Deploy the Chatfuel wizard app to Vercel, your own Node.js server or Docker, decide who may reach it, and fix the two setups the server refuses to run.
Last updated on
The app the Chatfuel wizard writes runs on Vercel, on your own Node.js server or in Docker, from the same code. The host decides who manages TLS and how long a WebSocket stays open; it does not decide who may use the app. That second question comes first, because the app talks to Chatfuel with your personal API token: whoever reaches the app acts with that token's access unless the auth module asks them to sign in.
How to deploy to Vercel
The wizard offers a Vercel deploy at the end of its run. Later, run it from the app directory:
npm run deployThe deploy script (scripts/deploy-vercel.mjs in your app) uses the Vercel CLI: it checks that you are signed in, links the project, pushes the variables from your .env to the Vercel project, deploys and asks the new deployment whether it came up configured. Your local .env file is never uploaded; the variables live in the Vercel project.
- Requests: the proxy runs as a Vercel function behind a rewrite of
/chatfuel/*. - Live updates: a WebSocket lasts as long as the function's maximum duration, then the app reconnects on its own.
- Path: the app is served from the domain root; a sub-path such as
/app/needs a Node.js server or Docker.
How to run the app on your own Node.js server
npm run build && npm startnpm run build builds the browser app and the server. npm start runs one Node.js server that serves the built app and the /chatfuel/* proxy on port 3000 (set PORT to change it, HOST to choose the interface). Put a reverse proxy with TLS, such as nginx or Caddy, in front of it.
How to run the app in Docker
The app ships with a Dockerfile that builds the app and starts the same Node.js server on port 3000. Pass the public VITE_* values as build arguments and every secret at runtime, for example with docker run --env-file .env. Never pass CHATFUEL_TOKEN or another secret as a build argument: build arguments stay readable in the image.
Who may reach the deployed app
Without the auth module the app has no sign-in: the proxy forwards every request it receives under your personal API token. Anyone who opens the URL can read conversations and contacts, send messages as the bot and change settings, even though the token itself never leaves the server. That setup suits a tool for your own team, kept off the public internet:
- on a VPN, a Tailscale or WireGuard network, or behind an office IP allowlist;
- behind your host's access control, such as Vercel Deployment Protection, nginx
auth_basicor Cloudflare Access; - on
localhostonly, which is whatnpm run devgives you.
An app that customers or staff open from the internet needs the auth module: each person signs in with your Supabase project before the proxy forwards anything. Add it with npx @chatfuel/wizard --embed. Wizard modules describes auth.
Which setups the server refuses to run
The server refuses two setups instead of warning about them. npm start prints REFUSING TO SERVE and does not start, the Vite dev server with --host stops before it mounts the proxy, and the Vercel function answers 503 with the code ProxyRefusedToServe to everything except the health check.
- No sign-in on a host that is not loopback. The server listens on all interfaces by default, and without
authanyone who can reach it drives your token. Fix it one of three ways: install theauthmodule, bind to loopback withHOST=127.0.0.1, or, when something in front of the app already authenticates every visitor, setCHATFUEL_OPEN_PROXY=1to confirm the open setup is intended. ALLOWED_ORIGINS=*on a host that is not loopback, with or without sign-in. SetALLOWED_ORIGINSto the origins that serve the app, separated by commas, for examplehttps://app.example.com.CHATFUEL_OPEN_PROXYdoes not cover this case.
How to check a deployment and protect the admin panel
/chatfuel/healthz answers on every host. It returns {"ok":true} with status 200 when the server is configured, or {"ok":false} with 503 when the configuration has a problem. An HTML page in reply means the path is wrong.
The admin module adds a panel at /admin that can change the whole account behind the token. ADMIN_PASSWORD is its only protection and must be at least 16 characters, or the panel refuses to run. With the auth module installed, also set SUPABASE_SERVICE_ROLE_KEY so every server instance shares one count of wrong passwords; on Vercel, where any warm instance may answer, the count is otherwise kept per instance.
Rules every host must follow
- Unknown paths answer with
index.html. Every screen has a real address, such as/deals, and a reload must return to it. The Node.js server and the shippedvercel.jsonalready do this; on nginx usetry_files $uri /index.html;. VITE_*variables are fixed at build time, every other variable is read by the server at runtime. After changing aVITE_*value, rebuild. A browser app built with Supabase settings and a server started without them, or the other way round, fails at sign-in withProxyAuthMisconfiguredorAuthSessionRequired.- Secrets stay on the server. Nothing named
VITE_*may hold a secret, because those values are compiled into the browser code.
Common issues
REFUSING TO SERVE: open mode on a host that is not loopback
The server runs without the auth module on an address others can reach. Install auth, set HOST=127.0.0.1, or set CHATFUEL_OPEN_PROXY=1 when your own access control already sits in front of the app.
REFUSING TO SERVE: ALLOWED_ORIGINS is '*'
Replace * with the exact origins of your app, for example ALLOWED_ORIGINS=https://app.example.com, and restart the server.
ADMIN_PASSWORD must be at least 16 characters. Set a longer one and restart.
The admin panel refuses a short password. Set a longer ADMIN_PASSWORD in the server environment, or in the Vercel project, and restart or redeploy.