Creating a Docker image
This guide will help you deploy the TanStack Start Prisma SaaS boilerplate to any Docker-compatible platform. Easily self-host your application with Docker.
If you want to self-host your application, you can deploy it using Docker.
This guide will help you create a Docker image for the TanStack Start Prisma SaaS boilerplate.
The app builds to a self-contained Nitro server bundle (apps/web/.output) that you run with node apps/web/.output/server/index.mjs.
Build-time vs runtime variables
Public VITE_* variables are inlined at build time by Vite, so they must be present in the build environment (e.g. Docker build args / build-time env). Server-only secrets (unprefixed, e.g. DATABASE_URL, BETTER_AUTH_SECRET, STRIPE_SECRET_KEY) are read from process.env at runtime and can be passed via --env-file when you run the container. This means the same image cannot swap public VITE_* values per environment without a rebuild.
1. Create a Dockerfile
Create a Dockerfile in the root of the project. The following multi-stage build prunes the monorepo with Turborepo, installs dependencies, builds the web app, and ships only the Nitro output:
# syntax=docker.io/docker/dockerfile:1FROM node:20-alpine AS base# Prune the monorepo for a smaller build contextFROM base AS prunerRUN apk add --no-cache libc6-compat && \ corepack enable pnpm && \ npm install -g turboWORKDIR /appCOPY . .RUN turbo prune web --docker# Install dependencies only when neededFROM base AS depsRUN apk add --no-cache libc6-compat && corepack enable pnpmWORKDIR /appCOPY --from=pruner /app/out/json/ ./COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yamlRUN pnpm install --frozen-lockfile# Build the appFROM base AS builderWORKDIR /appRUN corepack enable pnpm && npm install -g turboCOPY --from=deps /app ./COPY --from=pruner /app/out/full/ ./# Public VITE_* values are inlined here. Pass them as build args/env if needed.RUN turbo run build --filter=web...# Production image — copy the Nitro server output onlyFROM base AS runnerWORKDIR /appENV NODE_ENV=production \ PORT=3000 \ HOST=0.0.0.0RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nodejs-user# The Nitro build output is self-contained (server + public assets)COPY --from=builder --chown=nodejs-user:nodejs /app/apps/web/.output ./apps/web/.outputUSER nodejs-userEXPOSE 3000# Healthcheck hits the built-in /api/healthcheck routeHEALTHCHECK --interval=90s --timeout=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/healthcheck || exit 1CMD ["node", "apps/web/.output/server/index.mjs"]Bundled docker generator
The repository ships a pnpm turbo gen docker generator (turbo/generators/templates/docker/) that produces this same Nitro-based Dockerfile (copying apps/web/.output and starting .output/server/index.mjs). It also adds the platform-specific lightningcss / @tailwindcss/oxide musl build deps. So you can either run the generator or copy the Dockerfile above by hand.
2. Build the Docker image
Run the following command to build the Docker image:
docker build -t tanstack-prisma .This command will build the Docker image and tag it as tanstack-prisma.
If you need to pass public VITE_* values at build time, supply them as build environment variables (e.g. with --build-arg and a corresponding ARG/ENV in the builder stage) so they are inlined into the bundle.
3. Run the Docker container
3.1. Prepare a runtime env file
Create an env file with your server-only secrets for the production environment (for example apps/web/.env.production.local), containing values such as DATABASE_URL, BETTER_AUTH_SECRET, billing keys, and SMTP credentials.
3.2. Run the Docker container
Run the following command to run the Docker container:
docker run -p 3000:3000 --env-file apps/web/.env.production.local tanstack-prismaThis command will run the Docker container and make the application available at http://localhost:3000.
Deploying the Docker image to a container registry
You can deploy the Docker image to a container registry by running the following command:
docker push <your-docker-image>This command will push the Docker image to the container registry.
When you deploy the Docker image to a container registry, you can pull it from the registry and run it on any platform that supports Docker.
If you're using the Github Container Registry, you can run the following command:
docker login ghcr.ioThen, tag your local image with the registry path:
docker tag tanstack-prisma ghcr.io/your-username/tanstack-prismaThen, push the Docker image to the Github Container Registry:
docker push ghcr.io/your-username/tanstack-prismaThen, you can pull the Docker image from the Github Container Registry and run it on any platform that supports Docker:
docker pull ghcr.io/your-username/tanstack-prismaThen, you can run the Docker container:
docker run -p 3000:3000 --env-file apps/web/.env.production.local ghcr.io/your-username/tanstack-prismaThis command will run the Docker container and make the application available at http://localhost:3000.
This workflow allows you to deploy the Docker image to a container registry, and then pull it from the registry and run it on any platform that supports Docker - such as a VPS, a cloud provider, or a container platform like Docker Swarm or Kubernetes.
Easy, right?