Deploy

Deploy

Run a Lumiose build in production: what the build writes, the settings a server needs, and a guide for each hosting target.

#What to deploy

lumiose build writes dist/ (Rendering modes):

  • dist/client: the browser assets (content-hashed, under assets/), the files of public/, and the prerendered pages, with .br and .gz copies of every text file of 1 kB or more.
  • dist/server/entry.js: the request handler for server-rendered pages, endpoints and form actions. It bundles your server code and the Lumiose runtime; other npm packages stay imports, so production needs the app's dependencies installed.

A site whose every route is prerendered builds no dist/server: any static host serves dist/client. Anything else needs a runtime for dist/server/entry.js. On Node, @lumiose/adapter-node serves both directories, from the server.ts that create-lumiose --template ssr writes (reference); the guides below run it on Docker, a VPS, Coolify, Fly.io, Render and Railway. dist/server/entry.js exports a web-standard { fetch } (the server build), which Bun and Deno serve as it is.

For Cloudflare, Vercel and Netlify, a deploy adapter in the Vite config writes the platform's output after the build: lumiose({ adapter: cloudflare() }), vercel() or netlify(). Those platforms serve dist/client themselves and compress on their own, so the build then writes no .br/.gz copies.

#Production settings

The Node adapter and the request handler read these environment variables:

Variable Set it to
NODE_ENV production. The adapter sets it when it is unset, before the build loads.
PORT The port to listen on. Default: 3000.
HOST The address to listen on. Default: every address, which a container needs. Behind a proxy on the same machine, 127.0.0.1.
ORIGIN The public origin, such as https://example.com, behind a proxy that terminates TLS or rewrites the host. Request URLs (url(), ctx.url) are built from it.
LUMIOSE_SECRET When the app signs cookies (signedCookie()): a random secret of at least 32 bytes (openssl rand -base64 32). Never commit it or bake it into an image.

#Guides

  • Docker: A multi-stage Dockerfile: node:24-alpine, production dependencies only, a non-root user and a health check.
  • VPS: The Node adapter under systemd or pm2, behind Caddy or nginx, and what the adapter already does for compression and caching.
  • Coolify: Deploy from the Dockerfile with Coolify's Dockerfile build pack.
  • Cloudflare Workers: @lumiose/adapter-cloudflare: the Worker, static assets, bindings as ctx.platform.env.
  • Vercel: @lumiose/adapter-vercel: the Build Output API, with a Node.js or Edge function.
  • Netlify: @lumiose/adapter-netlify: the site and a Netlify Function.
  • Bun: bun dist/server/entry.js, static files included.
  • Deno: deno serve dist/server/entry.js, static files included.
  • Fly.io, Render, Railway: The Node adapter on container and Node hosts.
  • Static hosts: A fully prerendered site on Cloudflare, Netlify, GitHub Pages, S3 with CloudFront or any file server.
  • AWS Lambda: A handler from Lambda's events to the server build's fetch.

#Verification

Each target's status says how far its guide has been checked:

  • Verified on the platform: deployed to the real service by hand, on the date given.
  • Tested locally: run on a development machine in the target's own runtime (a container, the platform's emulator), by the script named or by hand; not on the real service.
  • Not tested: written from the platform's documentation only.
  • Planned: no guide yet.
Target Guide Status Checked What was checked
Docker Docker Tested locally 2026-10-12 The Dockerfile on the page, on a [email protected] --template ssr project with packages from npm: builds, healthy, serves pages (brotli) and immutable assets, runs as node, stops in under a second. Docker 29.8, linux/arm64. The npm variant is not tested.
Docker, bookshelf sample Docker Tested locally 2026-10-12 examples/ssr-bookshelf/e2e/docker-smoke.ts (local only, not in CI): health, a streamed page compressed on the fly, database queries, a sign-in form post, prerendered pages, immutable hashed assets served from their .br copies, the node user, no dev dependency in the image, a prompt stop. Docker 29.8, linux/arm64.
VPS: systemd VPS Tested locally 2026-10-12 The unit on the page ran the npm project under systemd 252 (Debian 12, in a privileged container): active, serving, stopped cleanly; systemd-analyze verify passes. Not on a VPS.
VPS: pm2 VPS Tested locally 2026-10-12 The ecosystem file on the page with pm2 6.0 (fork mode) in a container: serves, inherits the shell's variables, survives pm2 save and pm2 resurrect. pm2 startup and cluster mode are not tested.
Caddy VPS Tested locally 2026-10-12 The Caddyfile on the page (Caddy 2.11, localhost with Caddy's local CA instead of a public certificate) in front of the bookshelf container: HTTP to HTTPS redirect, HTTP/2, streamed chunks not held back, brotli and gzip passed through unchanged (with encode too), immutable assets, a form post and a cross-site 403.
nginx VPS Tested locally 2026-10-12 The config on the page (nginx 1.31, a self-signed certificate in place of certbot's) in front of the bookshelf container: nginx -t, the same checks as Caddy (with gzip on too); streaming also with nginx's default proxy_buffering on.
Coolify Coolify Not tested Not deployed to a Coolify instance yet. It builds the Dockerfile tested above; the steps follow Coolify v4's documentation.
Cloudflare Workers Cloudflare Tested locally 2026-10-13 examples/ssr-deploy, pnpm e2e:cloudflare (local only, not in CI) in [email protected] dev (workerd): prerendered page and assets with _headers (defaults, lumiose({ headers }), immutable hashed assets, not for a public/assets/ file), a streamed server page with a CSP nonce, endpoints with ctx.platform (a vars binding, CF-Connecting-IP), an npm package, a form post to a prerendered page and a cross-site 403, the not-found page, waitUntil. process.env holds vars with nodejs_compat.
Cloudflare, static site Static hosts Tested locally 2026-10-12 examples/ssg-travel-journal's dist/client by hand in [email protected] dev (assets only, not_found_handling: "404-page") and wrangler pages dev: pages, _headers, 404.html with status 404. Both serve the .br/.gz copies as plain files, hence the advice to leave them out.
Vercel, Node.js runtime Vercel Tested locally 2026-10-12 pnpm e2e:vercel (local only): config.json and .vc-config.json checked against the Build Output API v3, the function directory copied out of the repository (npm package traced in), then served by a Node.js harness that follows the routes and calls handler(req, res) as Vercel's launcher does: the same checks as Cloudflare. A harness, not Vercel's router or runtime.
Vercel, Edge runtime Vercel Tested locally 2026-10-12 pnpm e2e:vercel --edge: the same validation and checks, with the bundled module (it imports only node:async_hooks) called in Node.js, not in Vercel's Edge runtime.
Netlify Netlify Tested locally 2026-10-12 pnpm e2e:netlify (local only) with [email protected] serve, which runs the build and bundles both functions: the same checks as Cloudflare, except two the local server cannot show: it sends max-age=0 for static files (the immutable rule is checked in _headers), and its invocations share no state, so waitUntil is not observed.
Netlify, static site Static hosts Tested locally 2026-10-12 examples/ssg-travel-journal's dist/client by hand with [email protected] serve: pages with and without the slash, _headers, 404.html with status 404.
Bun Bun Tested locally 2026-10-12 pnpm e2e:runtimes bun (local only): [email protected] dist/server/entry.js, the same checks as Cloudflare (static files from dist/client, ctx.platform.clientAddress from Bun). The Dockerfile on the page is not tested.
Deno Deno Tested locally 2026-10-12 pnpm e2e:runtimes deno (local only): [email protected] serve --allow-net --allow-read --allow-env, the same checks. Deno Deploy is not tested.
Fly.io Fly.io Not tested Runs the Docker image tested above; fly.toml follows Fly's documentation.
Render Render Not tested Settings from Render's documentation; the build and start commands are the Node adapter's, tested on a VPS.
Railway Railway Not tested Settings from Railway's documentation; the build and start commands are the Node adapter's.
GitHub Pages Static hosts Not tested The workflow follows GitHub's Pages actions.
S3 and CloudFront Static hosts Not tested The commands follow AWS's documentation.
AWS Lambda AWS Lambda Not tested Not run in Lambda. The handler on the page was called in Node.js 26 with Function URL (payload 2.0) events against the sample's build: an endpoint, a streamed page (buffered), a form post, the not-found page, a static file.