Docker Compose: Multi-Container Apps from Scratch
Tech Setup1 min read
TS
Published August 2, 2026 · Editorial policy

What is Docker Compose?
Docker Compose defines and runs multi-container applications. With one YAML file, you start databases, caches, web servers, and app servers — all networked together.
Installation
Docker Desktop includes Compose. Verify:
docker compose version
# Docker Compose version v2.x.x
Basic Example: Web + Database
docker-compose.yml
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
depends_on:
- db
volumes:
- .:/app
- /app/node_modules
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
postgres_data:
Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
Start Everything
docker compose up -d
Essential Commands
| Command | Purpose |
|---|---|
docker compose up -d | Start in background |
docker compose down | Stop and remove containers |
docker compose ps | List running containers |
docker compose logs web | View logs for service |
docker compose logs -f | Follow all logs |
docker compose exec web sh | Shell into container |
docker compose build | Rebuild images |
docker compose restart web | Restart specific service |
docker compose stop | Stop without removing |
docker compose pull | Update base images |
Common Patterns
Environment Variables
Option 1: env_file
services:
web:
env_file:
- .env
Option 2: inline
services:
web:
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
env_file:
- .env
Networks
services:
web:
networks:
- frontend
db:
networks:
- frontend
- backend
cache:
networks:
- backend
networks:
frontend:
backend:
Services on the same network can communicate by service name.
Health Checks
services:
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
web:
build: .
depends_on:
db:
condition: service_healthy
Volumes
services:
db:
volumes:
# Named volume (persistent)
- postgres_data:/var/lib/postgresql/data
# Bind mount (local directory)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
postgres_data:
Ports
services:
web:
ports:
# host:container
- "3000:3000"
# Bind to localhost only
- "127.0.0.1:3000:3000"
# Random host port
- "3000"
Full-Stack Example
Node.js + PostgreSQL + Redis + Nginx
services:
app:
build:
context: .
dockerfile: Dockerfile
environment:
NODE_ENV: production
DATABASE_URL: postgresql://postgres:secret@db:5432/appdb
REDIS_URL: redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
networks:
- backend
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- backend
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
networks:
- backend
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- backend
volumes:
pgdata:
networks:
backend:
Development vs Production
Override File
# docker-compose.yml (base)
services:
app:
build: .
environment:
NODE_ENV: development
# docker-compose.prod.yml (production overrides)
services:
app:
image: myregistry/app:latest
environment:
NODE_ENV: production
deploy:
replicas: 3
resources:
limits:
cpus: "1.0"
memory: 512M
Commands
# Development
docker compose up -d
# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Debugging
View Logs
# All services
docker compose logs -f
# Specific service
docker compose logs -f app
# Last 100 lines
docker compose logs --tail 100 app
Inspect Network
docker network inspect tech-setup_default
Execute Commands
# Shell into running container
docker compose exec app sh
# Run one-off command
docker compose exec app node scripts/migrate.js
# Database shell
docker compose exec db psql -U postgres -d appdb
Cleanup
# Remove stopped containers
docker compose down
# Remove containers + volumes
docker compose down -v
# Remove containers + images
docker compose down --rmi all
# Prune everything
docker system prune -af
Performance Tips
- Use .dockerignore to exclude
node_modules,.git - Layer caching: copy package.json before source code
- Named volumes for databases (not bind mounts)
- Multi-stage builds for smaller production images
- Use
--no-cacheonly when needed (docker compose build --no-cache)


