Docker: From Dev to Production
Most SaaS boilerplates assume Vercel deployment — push to Git, Vercel builds and deploys. Simple, but limiting. What if you need to deploy to AWS, on-premises, or behind a corporate firewall?
Docker support means your SaaS runs anywhere a container runs — AWS ECS, Google Cloud Run, Azure Container Apps, Kubernetes, a $5 VPS, or your client's private cloud. It also means consistent development environments and reproducible builds.
Three boilerplates take Docker seriously: Enterprise Boilerplate (Next.js), Bedrock (Next.js), and SaaSrock (Remix). Each provides Dockerfiles, docker-compose configurations, and production deployment patterns.
TL;DR
Bedrock ($395-$995) has the most production-ready Docker setup — multi-stage builds, Docker Compose for development, CI/CD pipeline integration, and Kubernetes-ready configurations. Enterprise Boilerplate ($199-$499) includes Docker with optimized Next.js builds and health check endpoints. SaaSrock ($149-$699) provides Docker Compose for its Remix app plus all dependent services (database, Redis, email). Choose Bedrock for enterprise Docker/K8s. Choose SaaSrock for all-services-in-Docker development.
Key Takeaways
- Multi-stage Docker builds reduce image size from 1GB+ to 100-200 MB. All three implement this correctly.
- Docker Compose for development gives every developer the same environment — database, Redis, mail server included.
- CI/CD integration varies. Bedrock includes GitHub Actions for build/test/deploy. Enterprise Boilerplate includes build pipelines. SaaSrock has basic CI.
- Health check endpoints are critical for container orchestrators. Bedrock and Enterprise Boilerplate include them. SaaSrock requires manual setup.
- Most boilerplates ignore Docker entirely. ShipFast, Supastarter, Makerkit, T3 Stack — deploy to Vercel or figure it out yourself.
Docker Feature Comparison
| Feature | Enterprise Boilerplate | Bedrock | SaaSrock |
|---|---|---|---|
| Dockerfile | ✅ Multi-stage | ✅ Multi-stage | ✅ Multi-stage |
| Docker Compose (dev) | ✅ | ✅ | ✅ Full stack |
| Docker Compose (prod) | ✅ | ✅ | ⚠️ Basic |
| Image size | ~150 MB | ~120 MB | ~200 MB |
| Health check endpoint | ✅ /api/health | ✅ /health | ⚠️ Manual |
| Readiness probe | ✅ | ✅ | ❌ |
| Environment variables | ✅ Docker secrets | ✅ Docker secrets | ✅ .env file |
| PostgreSQL included | ✅ Compose | ✅ Compose | ✅ Compose |
| Redis included | ✅ Compose | ✅ Compose | ⚠️ Optional |
| CI/CD pipeline | ✅ GitHub Actions | ✅ GitHub Actions | ⚠️ Basic |
| K8s manifests | ❌ | ✅ | ❌ |
| Nginx/proxy config | ✅ | ✅ | ❌ |
| SSL/TLS config | ⚠️ Via proxy | ✅ | ❌ |
| Log management | ⚠️ stdout | ✅ Structured | ⚠️ stdout |
| Graceful shutdown | ✅ | ✅ | ⚠️ Basic |
Bedrock's Docker Setup
# Multi-stage build — production-optimized
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat
FROM base AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable pnpm && pnpm install --frozen-lockfile
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN pnpm build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]
# docker-compose.yml — Development
services:
app:
build: .
ports: ["3000:3000"]
environment:
DATABASE_URL: postgres://postgres:postgres@db:5432/app
REDIS_URL: redis://redis:6379
depends_on:
db: { condition: service_healthy }
redis: { condition: service_started }
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_PASSWORD: postgres
volumes: ["pgdata:/var/lib/postgresql/data"]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes: ["redis-data:/data"]
volumes:
pgdata:
redis-data:
Bedrock's setup includes health checks on the database, proper service dependencies, and volume persistence. The multi-stage build produces images under 120 MB.
When Docker Matters
You Need Docker If:
- Deploying to AWS/GCP/Azure — ECS, Cloud Run, AKS all use containers
- Enterprise customers require on-premises — Docker runs behind any firewall
- Team development consistency — everyone runs the same stack, same versions
- CI/CD pipeline — build once, deploy to staging and production
- Multiple services — database, Redis, background workers, main app
- Compliance requirements — reproducible, auditable builds
You Don't Need Docker If:
- Vercel/Netlify is sufficient — serverless deployment, no containers needed
- Solo developer — your local environment is the only environment
- Simple SaaS — single app, managed database, no background workers
When to Choose Each
Choose Bedrock If:
- Enterprise deployment — K8s manifests, health probes, structured logging
- Multi-environment pipelines — build → staging → production with the same image
- Compliance matters — auditable builds, Docker security scanning
Choose Enterprise Boilerplate If:
- Next.js + Docker — optimized standalone builds, health checks, CI/CD
- Moderate complexity — PostgreSQL + Redis + app in Docker Compose
Choose SaaSrock If:
- Remix + Docker — the only Remix boilerplate with proper Docker support
- Full development stack — database, email server, and app all containerized
Docker and Enterprise Sales
Docker support matters beyond technical preference — it's often a pre-sales requirement. Mid-market and enterprise prospects frequently ask:
- "Can this be deployed on our AWS infrastructure?"
- "Do you support air-gapped installation behind our firewall?"
- "Can we audit what's in your container?"
- "Does your deployment support our existing Kubernetes cluster?"
Answering "yes" to these questions requires Docker. For B2B SaaS targeting companies with IT governance requirements, starting with a Docker-ready boilerplate avoids expensive retrofitting when the first enterprise deal requires it.
Bedrock addresses this explicitly in its documentation — the Docker setup is designed with enterprise sales in mind. Kubernetes manifests, structured logging (JSON), and health probes for readiness and liveness are all present because enterprise deployment requirements demand them.
CI/CD Pipeline Integration
Docker becomes most valuable when connected to a CI/CD pipeline:
# .github/workflows/deploy.yml — Bedrock's GitHub Actions pattern
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t my-saas:${{ github.sha }} .
docker tag my-saas:${{ github.sha }} my-saas:latest
- name: Run tests in container
run: docker run --rm my-saas:${{ github.sha }} npm test
- name: Push to registry
run: |
docker push registry.example.com/my-saas:${{ github.sha }}
docker push registry.example.com/my-saas:latest
- name: Deploy to Railway/Render/K8s
run: |
railway up --detach # or kubectl apply, or render deploy
Bedrock's GitHub Actions workflow is production-ready — it builds once, runs tests against the built image, and deploys the same image to staging then production. This "build once, deploy anywhere" pattern is the core Docker promise.
Deploying Docker Containers to Railway
Railway is the easiest path for Docker deployments without Kubernetes complexity:
# Railway CLI deployment
npm install -g @railway/cli
railway login
# Initialize project
railway init
# Deploy the Dockerfile
railway up
# Set environment variables
railway variables set DATABASE_URL="postgresql://..."
railway variables set STRIPE_SECRET_KEY="sk_live_..."
# View logs
railway logs
Railway detects the Dockerfile automatically and builds the container on its infrastructure. No Kubernetes knowledge required. For the 95% of SaaS products that don't need multi-region K8s deployments, Railway provides a good middle ground between Vercel's simplicity and full container orchestration.
Docker Production Checklist
Running Docker in development is straightforward. Production introduces additional requirements that the boilerplates handle to varying degrees. Before shipping a Dockerized SaaS:
- Image scanning: Run Trivy or Docker Scout against your built image before each deployment — critical CVEs in base images surface regularly. Bedrock's CI/CD pipeline includes this step by default.
- Non-root user: Containers should run as a non-root user. Bedrock's Dockerfile creates a dedicated
nextjsuser with UID 1001. Enterprise Boilerplate follows the same pattern. Running as root inside a container is a security misconfiguration that fails many compliance audits. - Secret management: Use Docker secrets or runtime-injected environment variables — never embed credentials in the image layer. Both Bedrock and Enterprise Boilerplate handle this correctly.
- Resource limits: Set memory and CPU limits in your Compose or Kubernetes manifests. Uncapped containers cause cascading failures when traffic spikes exhaust available memory on the host.
- Graceful shutdown: Handle
SIGTERMin your application code. Next.js standalone output does this automatically; custom Node.js servers require explicit signal handling to drain in-flight requests before exit.
Key Takeaways
- Bedrock has the most complete Docker setup: multi-stage builds (~120 MB images), health probes, K8s manifests, and GitHub Actions CI/CD
- Enterprise Boilerplate ships a solid Docker config with health check endpoints — good for AWS ECS or Google Cloud Run deployments
- SaaSrock provides the most complete local development Docker Compose (all services included) but weaker production K8s support
- Most mainstream boilerplates (ShipFast, T3, Makerkit) skip Docker entirely — plan to add it yourself or start with a Docker-ready option
- Docker readiness is increasingly important for B2B enterprise sales — it's often asked in procurement questionnaires
- Railway or Render provide the fastest path from Dockerfile to production without Kubernetes operational overhead
- Docker image size matters for cost: a 200 MB image deployed 10 times per day on a CI/CD pipeline costs more in bandwidth and build time than a 120 MB multi-stage image; Bedrock's sub-120 MB output is meaningful at scale
- Most SaaS boilerplates that skip Docker can be containerized in 3-4 hours with a standard multi-stage Dockerfile; the investment is lower than it appears, though you lose the pre-built CI/CD configurations that Bedrock provides
- Enterprise procurement checklists regularly ask: "Is your application deployable as a container?" Having a working Dockerfile is a sales enablement asset, not just a technical preference
- For teams starting with Vercel and later needing Docker: the transition is easier when your application code doesn't assume Vercel-specific features like Edge Functions or Serverless Functions for core logic — keep those paths optional and the Docker migration is significantly cleaner
- The three boilerplates with serious Docker support (Bedrock, Enterprise Boilerplate, SaaSrock) all share a common pattern: they were built for B2B customers who have deployment requirements, not for indie hackers deploying to Vercel
- Container security scanning is an underrated part of Docker production readiness: Bedrock's CI/CD pipeline includes Trivy vulnerability scanning on the built image before deployment — a practice that most teams add only after a security incident prompts it
Adding Docker to a Non-Docker Boilerplate
Most SaaS boilerplates don't ship Docker support. If you've chosen ShipFast, Supastarter, or T3 Stack for their SaaS features and need Docker for deployment, the migration is achievable in 3-4 hours with a standard multi-stage build pattern.
The core challenge is Next.js standalone output. By default, Next.js includes development dependencies in the build output, producing images over 1GB. Adding output: 'standalone' to next.config.js tells Next.js to produce a minimal server bundle — only the files needed to run in production. The multi-stage Dockerfile from Bedrock's example works for any Next.js app with this setting:
Stage 1 (deps): Install dependencies from lockfile. Stage 2 (builder): Copy deps, copy source, run prisma generate, run next build. Stage 3 (runner): Copy only the standalone output and static assets. The final image size drops from 1GB+ to 100-200 MB.
For apps using Prisma, the build stage must include npx prisma generate to create the Prisma Client before building the Next.js app. The generated Prisma Client is included in the standalone output automatically.
Environment variables for Next.js Docker deployments follow a specific pattern: variables needed at build time (like NEXT_PUBLIC_* variables) must be ARGs in the Dockerfile or available during docker build. Variables that change between environments (DATABASE_URL, Stripe keys) should be runtime environment variables passed via -e flags or Docker secrets — never baked into the image.
Why Most Boilerplates Skip Docker
Understanding why the majority of SaaS boilerplates don't include Docker helps set realistic expectations. The boilerplate creators optimizing for "fastest path to running locally" default to Vercel because:
Vercel handles the build and deployment pipeline automatically from a GitHub push. There's no Dockerfile to maintain, no container registry to configure, no CI/CD pipeline to write. For the 90% of SaaS products that deploy to Vercel or Netlify, Docker is genuinely unnecessary complexity.
Docker becomes necessary when the target audience includes enterprise customers with deployment requirements, on-premises installation needs, or environments where Vercel isn't an option. Bedrock, Enterprise Boilerplate, and SaaSrock are explicitly targeting the enterprise and B2B market where these requirements are common — that's why they invest in Docker support.
The practical implication: if you're building a consumer SaaS or developer tool targeting individual users, Vercel deployment is almost certainly the right choice and Docker is overengineering. If you're building B2B SaaS where a prospect will ask "can this run in our AWS account," Docker readiness is a selling point that Bedrock provides and ShipFast doesn't.
For the full B2B SaaS boilerplate comparison including enterprise features beyond Docker, see the enterprise boilerplate review and the best SaaS boilerplates guide. For the build vs deploy trade-offs when Docker isn't required, see the buy vs build SaaS analysis. For how the best serverless alternatives compare to Docker-based deployments, see the best serverless boilerplates guide.
Docker containerization adds reproducibility to any deployment workflow, ensuring that the environment your boilerplate runs in locally matches staging and production exactly. The configuration overhead is front-loaded: once your Dockerfile and compose files are in place, spinning up new environments takes minutes rather than hours.
The Docker vs Vercel decision often reflects team culture as much as technical requirements: DevOps-comfortable teams default to Docker; product-focused teams default to Vercel. Both approaches reach production successfully — the friction is in the operational experience, not the final result.
Compare Docker support across 50+ boilerplates on StarterPick.
Review Bedrock and compare alternatives on StarterPick.
See our Docker vs Vercel guide for when to use each deployment approach.