docker
Full-Stack Docker Compose (Node + PostgreSQL + Redis)
Complete Docker Compose config for a full-stack app with Node.js API, PostgreSQL database, and Redis cache.
Overview
A complete Docker Compose stack for a typical full-stack web application. Includes a Node.js API server, PostgreSQL for persistent storage, Redis for caching and sessions, and proper service dependencies with health checks.
Configuration
# docker-compose.yml
services:
# ── Node.js API Server ──
api:
build:
context: .
dockerfile: Dockerfile
target: development
container_name: fullstack-api
restart: unless-stopped
ports:
- "3000:3000" # API port
- "9229:9229" # Debugger
environment:
NODE_ENV: development
PORT: 3000
DATABASE_URL: postgresql://appuser:${DB_PASSWORD:-secret}@postgres:5432/appdb
REDIS_URL: redis://:${REDIS_PASSWORD:-redispass}@redis:6379/0
SESSION_SECRET: ${SESSION_SECRET:-dev-session-secret}
volumes:
- .:/app
- /app/node_modules
command: npx nodemon --inspect=0.0.0.0:9229 src/index.ts
init: true
depends_on:
postgres:
condition: service_healthy # Wait for DB to accept connections
redis:
condition: service_healthy # Wait for Redis to respond
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
# ── PostgreSQL Database ──
postgres:
image: postgres:16-alpine
container_name: fullstack-postgres
restart: unless-stopped
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
POSTGRES_DB: appdb
ports:
- "5432:5432" # Expose for local DB tools
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/01-init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
networks:
- backend
# ── Redis Cache ──
redis:
image: redis:7-alpine
container_name: fullstack-redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD:-redispass} --maxmemory 128mb --maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD:-redispass}", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
# ── Database Migrations ──
migrate:
build:
context: .
dockerfile: Dockerfile
target: development
profiles:
- tools
environment:
DATABASE_URL: postgresql://appuser:${DB_PASSWORD:-secret}@postgres:5432/appdb
command: npm run db:migrate
depends_on:
postgres:
condition: service_healthy
networks:
- backend
# ── pgAdmin (optional) ──
pgadmin:
image: dpage/pgadmin4:latest
container_name: fullstack-pgadmin
profiles:
- tools
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin
ports:
- "5050:80"
depends_on:
postgres:
condition: service_healthy
networks:
- backend
volumes:
postgres_data:
redis_data:
networks:
backend:
driver: bridge
Key Options Explained
depends_onwithcondition— Ensures the API starts only after PostgreSQL and Redis pass their health checks, preventing connection errors on startup.DATABASE_URL/REDIS_URL— Connection strings use Docker service names (postgres,redis) as hostnames. Docker DNS resolves these within the network.networks: backend— All services share a custom bridge network. Services can reach each other by name but are isolated from other Docker networks.profiles: [tools]— Migration runner and pgAdmin only start when explicitly requested:docker compose --profile tools run migrate.- Anonymous volume
/app/node_modules— Prevents host node_modules from shadowing the container’s installed dependencies.
Common Modifications
- Add a frontend: Include a separate service for React/Next.js with its own build context, port (e.g.,
3001), and volume mounts. - Add Nginx reverse proxy: Place an Nginx service in front of the API with
proxy_pass http://api:3000for SSL termination or load balancing. - Production override: Create
docker-compose.prod.ymlwithdocker compose -f docker-compose.yml -f docker-compose.prod.yml upto override dev settings. - Seed data: Add a
seedservice similar tomigratethat runsnpm run db:seedon demand. - Shared
.envfile: Create a.envfile in the project root withDB_PASSWORD,REDIS_PASSWORD, andSESSION_SECRETvalues.