← Back to blog
·6 min read

Deploying to production

LaunchApp is designed to deploy as two separate services: the React Router 7 web app and the Hono API server. Both are standard Node.js processes that you can run on any platform that supports a Dockerfile or a Node runtime — Railway, Fly.io, Render, AWS ECS, or your own Kubernetes cluster.

Build once, ship everywhere

Before deploying, build the entire monorepo with pnpm build. Turborepo caches build artefacts and only rebuilds packages that have changed, so incremental builds are fast. The web app output lands in apps/web/build and the API in packages/api/dist.

The included Dockerfile is a multi-stage build that:

  1. Installs dependencies using the frozen lockfile.
  2. Runs pnpm build --filter web in a build stage.
  3. Copies only the production output and node_modules into the final image.

This keeps the runtime image lean and avoids shipping dev dependencies to production.

Environment variables

Set all required environment variables on your hosting platform:

  • DATABASE_URL — production PostgreSQL connection string
  • BETTER_AUTH_SECRET — a long random string (use openssl rand -base64 32)
  • BETTER_AUTH_URL — the public URL of your API
  • Optional integrations like STRIPE_SECRET_KEY or SENTRY_DSN

The .env.example file lists every variable the templates read. If you add a new one, update .env.example in the same PR so teammates see it on the next clone.

Running migrations

Run database migrations against the production database with pnpm db:migrate before starting the new release. The migration command reads DATABASE_URL from the environment, so no code changes are needed between environments.

For zero-downtime deployments, consider running migrations in a pre-deploy step and keeping the old and new schema compatible until the old release is drained. The Pulumi program in infrastructure/pulumi can provision the cloud resources if you prefer infrastructure-as-code.

Health checks and observability

The API exposes /api/health which the Dockerfile's HEALTHCHECK directive uses. Point your load balancer at the same endpoint. For observability, wire up Sentry via SENTRY_DSN and the optional PostHog or Vercel Analytics integrations — both ship as separate @repo/* packages so you can drop them in without refactoring the rest of the app.