raxel.studio

Docs / Deployment

Deployment

The starter ships with a production Dockerfile and a tiny in-process proxy. Two paths are supported: Vercel-style serverless, and self-hosted via Docker.

Self-hosted via Docker #

The docker/ directory contains:

  • Dockerfile — multi-stage build, outputs a small Node image
  • nginx.conf (if present) — reverse-proxy config for terminating TLS

A docker-compose.yml at the repo root runs the app alongside Postgres for local production-like testing:

docker compose up --build

To deploy to a VPS:

  1. Build the image: docker build -t my-app .
  2. Push it to your registry.
  3. Run it on the host, passing env vars: docker run -e DATABASE_URL=... -e BETTER_AUTH_SECRET=... -p 3000:3000 my-app.
  4. Front it with whatever TLS terminator you prefer (Caddy, nginx, Cloudflare).

proxy.ts #

The proxy.ts file is a tiny Node server that some hosts (e.g. self-hosted Coolify, Dokku) want as the entry point. It binds to PORT, hands off to Next, and adds a /health endpoint. If your host doesn't need a custom entry, ignore it.

Env vars #

Required in every environment:

  • DATABASE_URL
  • BETTER_AUTH_SECRET
  • BETTER_AUTH_URL (set this to your canonical URL in prod)

Optional:

  • RESEND_API_KEY (transactional email)
  • NODE_ENV=production (set by your host usually)

Build-time env #

Some of these are read at build time, not runtime. If you change BETTER_AUTH_URL, you need to rebuild — not just restart. Bake your prod URL in at CI time.

Serverless (Vercel et al.) #

The starter is App Router-native and runs fine on Vercel. A few notes:

  • Set DATABASE_URL to a pooler-aware connection string (Neon, Supabase poolers, etc.). Direct connections will exhaust quickly.
  • The proxy.ts file is unused on serverless.
  • Cron, scheduled tasks, and background jobs are not included — use your platform's primitives (Vercel Cron, Inngest, etc.).

Migrations in CI #

Run migrations as a separate step before deploying the app:

pnpm db:migrate

If you skip this, the app boots against an out-of-date schema and you'll get runtime errors on the first request that touches the changed tables.