Docker Compose is for local development and simple deployments. Kubernetes is for production at scale. Compose is where you prototype — once it works locally, you translate services to K8s Deployments/Services for production.
🎼 Docker Compose for Local Multi-Container Apps
Define and run multi-container applications with a single YAML file — web server, database, cache, and workers all up with one command.
🧒 Simple Explanation (ELI5)
Running a web app manually means typing 5 long docker run commands with networks, volumes, and port mappings — every time. Docker Compose is a conductor's score: you write down all the musicians (services), their instruments (images), and how they interact, then tap the baton and the whole orchestra starts at once.
🔧 docker-compose.yml Anatomy
version: "3.9"
services:
# --- Web API ---
api:
build:
context: .
dockerfile: Dockerfile
image: myapp-api:dev
ports:
- "3000:3000" # host:container
environment:
- NODE_ENV=development
- DB_HOST=db # reaches postgres container by service name
- DB_PORT=5432
- REDIS_URL=redis://cache:6379
volumes:
- ./src:/app/src # bind mount for hot-reload in dev
depends_on:
db:
condition: service_healthy # wait for DB health check
cache:
condition: service_started
networks:
- app-network
restart: unless-stopped
# --- PostgreSQL Database ---
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: secret # in dev only; use secrets in prod
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
# --- Redis Cache ---
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
networks:
- app-network
volumes:
pgdata: # named volume persists DB data across restarts
networks:
app-network:
driver: bridge💻 Compose Commands
# Start all services (detached) docker compose up -d # Start and rebuild images first docker compose up -d --build # View status of all services docker compose ps # View logs from all services (follow) docker compose logs -f # View logs from one service docker compose logs -f api # Stop all services (preserve volumes) docker compose down # Stop and remove volumes too (resets data) docker compose down -v # Run a one-off command in a service container docker compose exec api /bin/sh docker compose exec db psql -U user -d myapp # Scale a service (multiple instances) docker compose up -d --scale api=3 # Pull latest images docker compose pull
🐛 Debugging Scenario
Problem: API fails to connect to DB on startup — "connection refused". API starts before the database is ready.
# Fix: use depends_on with health check condition
services:
api:
depends_on:
db:
condition: service_healthy # wait until DB passes health check
db:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
retries: 10
# Note: depends_on only ensures order of start, not "app readiness"
# Your app should also have retry logic for DB connections🎯 Interview Questions
docker compose up creates and starts containers. If containers already exist but are stopped, it recreates them if the config has changed, or starts them as-is. docker compose start only starts already-created but stopped containers — it does NOT create new containers or apply config changes. Use up for most workflows.
Docker Compose creates a user-defined bridge network for all services in the same compose file. Docker's embedded DNS server resolves service names to container IPs. A service named db is reachable at hostname db from any other service in the same Compose file, without any manual network configuration.
Use Compose file layering: a docker-compose.yml base with shared config, a docker-compose.override.yml that is auto-applied in development (dev volumes, debug ports), and a docker-compose.prod.yml for production-specific settings. Run production with: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d. This keeps DRY config with environment-specific overrides.
📋 Summary
- Docker Compose defines multi-container apps in a single
docker-compose.ymlfile. - Services find each other by service name via Docker's embedded DNS.
- Use
depends_on+healthcheckto control startup order and readiness. - Named volumes persist data; bind mounts enable hot-reload in development.
- Use
docker compose up -d --buildto rebuild and redeploy on code changes.