Docker Compose for NestJS + Angular Development
One Command to Rule Them All
New developer joins the team. They clone the repo, run docker compose up, and everything works. No installing PostgreSQL, no configuring Redis, no version mismatches. That is the promise of Docker Compose, and it is essential for any serious SaaS development workflow.
The Complete docker-compose.yml
version: '3.8'
services:
api:
build:
context: .
dockerfile: apps/api/Dockerfile
target: development
volumes:
- ./apps/api/src:/app/apps/api/src
- ./libs:/app/libs
ports:
- '3000:3000'
- '9229:9229' # Debug port
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/saas
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
command: npx nx serve api
web:
build:
context: .
dockerfile: apps/web/Dockerfile
target: development
volumes:
- ./apps/web/src:/app/apps/web/src
ports:
- '4200:4200'
command: npx nx serve web
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: saas
ports:
- '5432:5432'
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
ports:
- '6379:6379'
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
Cloudrix SaaS Starter ships with this pre-configured and tested.
Skip weeks of boilerplate — auth, payments, multi-tenancy, and deployment included out of the box.
Try the live demo →Multi-Stage Dockerfiles
Use multi-stage builds to keep images small in production while supporting hot reload in development:
# Base stage
FROM node:20-alpine AS base
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
# Development stage
FROM base AS development
COPY . .
EXPOSE 3000 9229
# Production stage
FROM base AS production
COPY . .
RUN pnpm nx build api --configuration=production
CMD ["node", "dist/apps/api/main.js"]
The target: development in docker-compose.yml tells Docker to stop at the development stage, giving you hot reload via volume mounts.
Hot Reload with Volume Mounts
The key to a good developer experience is fast feedback. Mount your source directories as volumes so changes are reflected instantly:
volumes:
- ./apps/api/src:/app/apps/api/src
NestJS's file watcher detects the changes and rebuilds. Angular's dev server does the same. No container restart needed.
Environment Variables
Never hard-code configuration. Use a .env file for local development:
DATABASE_URL=postgres://postgres:postgres@db:5432/saas
REDIS_URL=redis://redis:6379
JWT_SECRET=local-dev-secret
STRIPE_SECRET_KEY=sk_test_...
Reference it in docker-compose.yml:
env_file:
- .env
Health Checks and Startup Order
PostgreSQL takes a few seconds to accept connections. Without a health check, your API container will crash on startup. The healthcheck and depends_on with condition: service_healthy in the compose file solves this cleanly.
Adding More Services
As your SaaS grows, add services to the same compose file:
maildev:
image: maildev/maildev
ports:
- '1080:1080' # Web UI
- '1025:1025' # SMTP
minio:
image: minio/minio
command: server /data --console-address ":9001"
ports:
- '9000:9000'
- '9001:9001'
MailDev catches all outgoing emails locally. MinIO provides S3-compatible storage. Both eliminate the need for external service accounts during development.
Production vs Development
Create a separate docker-compose.prod.yml that overrides development settings:
services:
api:
build:
target: production
volumes: [] # No source mounts
restart: unless-stopped
Deploy with docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d.
SaaS Starter includes a production-ready Docker setup. See the deployment feature for details.
Get Started Today
All of these patterns are fully implemented in SaaS Starter. Skip 2-6 weeks of setup.
- Free version: GitHub
- Pro ($249): Get SaaS Starter Pro