By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(Services, Networks, Volumes, Environment)
Docker Compose is your local development and small-scale deployment Swiss Army knife. It lets you define and run multi-container Docker applications with a single YAML file (docker-compose.yml). Think of it as a blueprint for your entire app stack—web server, database, cache, and all—so you can spin it up with one command (docker-compose up).
docker-compose.yml
docker-compose up
Why this matters in production:- Reproducibility: No more "works on my machine" bugs. Your docker-compose.yml is the single source of truth for your app’s environment.- Isolation: Each service runs in its own container, with its own network and storage, just like in Kubernetes (but simpler).- CI/CD & Testing: Use Compose to run integration tests in a near-production environment before deploying to Kubernetes.- Legacy App Lift-and-Shift: If you inherit a monolith that "just works" on a VM, Compose lets you containerize it without rewriting everything.
Real-world scenario:You’re a DevOps engineer at a startup. The dev team just handed you a Python Flask app with a Redis cache and PostgreSQL DB. They run it locally with python app.py, but it crashes in staging because the DB version is wrong. Your mission: Containerize the entire stack with Compose so it runs identically everywhere—local dev, CI, and staging.
python app.py
services
web
db
redis
networks
NetworkPolicy
volumes
postgres_data
PersistentVolume
PersistentVolumeClaim
environment
POSTGRES_PASSWORD=secret
.env
ConfigMap
Secret
depends_on
healthcheck
ports
"8080:80"
Ingress
build
Dockerfile
restart
always
on-failure
restart: unless-stopped
restartPolicy
Create this directory structure:
flask-app/ ├── app/ │ ├── __init__.py │ ├── app.py │ └── requirements.txt ├── docker-compose.yml └── .env
app/app.py:
app/app.py
from flask import Flask import redis import psycopg2 import os app = Flask(__name__) # Redis redis_host = os.getenv("REDIS_HOST", "redis") redis_port = os.getenv("REDIS_PORT", 6379) r = redis.Redis(host=redis_host, port=redis_port, db=0) # PostgreSQL pg_host = os.getenv("POSTGRES_HOST", "db") pg_user = os.getenv("POSTGRES_USER", "postgres") pg_password = os.getenv("POSTGRES_PASSWORD", "postgres") pg_db = os.getenv("POSTGRES_DB", "postgres") def get_db_connection(): conn = psycopg2.connect( host=pg_host, user=pg_user, password=pg_password, database=pg_db ) return conn @app.route("/") def hello(): # Increment counter in Redis r.incr("hits") hits = r.get("hits").decode("utf-8") # Query PostgreSQL conn = get_db_connection() cur = conn.cursor() cur.execute("SELECT version();") pg_version = cur.fetchone()[0] cur.close() conn.close() return f"Hello! Redis hits: {hits}. PostgreSQL version: {pg_version}" if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)
app/requirements.txt:
app/requirements.txt
flask==2.0.1 redis==4.1.0 psycopg2-binary==2.9.3
version: "3.8" services: web: build: . ports: - "5000:5000" environment: - REDIS_HOST=redis - POSTGRES_HOST=db - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=${POSTGRES_DB} depends_on: - redis - db networks: - app_network redis: image: "redis:alpine" ports: - "6379:6379" volumes: - redis_data:/data networks: - app_network db: image: "postgres:13-alpine" environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=${POSTGRES_DB} volumes: - postgres_data:/var/lib/postgresql/data networks: - app_network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] interval: 5s timeout: 5s retries: 5 volumes: redis_data: postgres_data: networks: app_network: driver: bridge
POSTGRES_USER=postgres POSTGRES_PASSWORD=mysecretpassword POSTGRES_DB=mydb
# Build and start the stack docker-compose up -d # Check logs docker-compose logs -f web # Verify it works curl http://localhost:5000 # Output: "Hello! Redis hits: 1. PostgreSQL version: PostgreSQL 13.4 on x86_64-pc-linux-musl, compiled by gcc (Alpine 10.3.1_git20210424) 10.3.1 20210424, 64-bit" # Stop the stack docker-compose down
# Stop the stack docker-compose down # Start it again docker-compose up -d # Check the counter (should increment) curl http://localhost:5000 # Output: "Hello! Redis hits: 2. PostgreSQL version: PostgreSQL 13.4..."
Why this works:- Volumes (redis_data, postgres_data) persist data between restarts.- The healthcheck ensures PostgreSQL is ready before web starts.- Environment variables are loaded from .env.
redis_data
.gitignore
POSTGRES_PASSWORD
docker scan
alpine
postgres:13-alpine
yaml deploy: resources: limits: cpus: "0.5" memory: 512M
bash docker system prune -a --volumes
postgres:latest
docker-compose.prod.yml
yaml healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000"] interval: 30s timeout: 10s retries: 3
docker-compose logs
bash docker-compose logs -f --tail=100 web
bash docker stats
postgres_data:/var/lib/postgresql/data
5432
latest
Trap: "80:8080" (host:container, not container:host).
"80:8080"
Volumes:
Trap: Using a bind mount (e.g., ./data:/var/lib/postgresql/data) for production (slower, less portable).
./data:/var/lib/postgresql/data
Environment Variables:
environment:
Trap: Hardcoding secrets in docker-compose.yml.
Networks:
db_network
web_network
Trap: Relying on the default network (all services can talk to each other).
Health Checks:
Challenge:You have a docker-compose.yml with a web service (Flask) and a db service (PostgreSQL). The web service crashes on startup because it tries to connect to db before PostgreSQL is ready. Fix it.
Solution:
services: web: depends_on: db: condition: service_healthy db: healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5
Why it works:- depends_on with condition: service_healthy waits for the db health check to pass.- The healthcheck ensures PostgreSQL is ready before web starts.
condition: service_healthy
docker-compose up -d
-d
docker-compose down
-v
docker-compose logs -f
docker-compose build
--no-cache
docker-compose ps
docker-compose exec web bash
environment: - POSTGRES_PASSWORD=secret
networks: - app_network
test: ["CMD", "curl", "-f", "http://localhost"]
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.