Docker Compose defines a multi-container application in one YAML file. This lab runs a small Python web API backed by Redis and demonstrates the core Compose workflow: build, validate, start, inspect, log, execute, stop, and remove.
1. Create the project
mkdir -p ~/compose-counter && cd ~/compose-counter
cat > requirements.txt <<'EOF'
flask==3.1.1
redis==6.2.0
gunicorn==23.0.0
EOF
cat > app.py <<'EOF'
import os
from flask import Flask
from redis import Redis
app = Flask(__name__)
r = Redis(host=os.getenv("REDIS_HOST", "redis"), decode_responses=True)
@app.get("/")
def index(): return {"visits": r.incr("visits")}
@app.get("/healthz")
def health(): return {"status": "ok"}
EOF
2. Build the web image
FROM python:3.13-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
RUN adduser -D -u 10001 app
USER app
EXPOSE 8000
CMD ["gunicorn", "--bind=0.0.0.0:8000", "--workers=2", "app:app"]
Save that block as Dockerfile, then create compose.yaml:
services:
web:
build: .
ports:
- "127.0.0.1:8000:8000"
environment:
REDIS_HOST: redis
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
redis:
image: redis:8-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
volumes:
redis-data:
3. Validate and start the stack
docker compose config
docker compose build --pull
docker compose up -d
docker compose ps
curl http://127.0.0.1:8000/
curl http://127.0.0.1:8000/
The counter should increase while Redis stores its value in the named volume.
4. Operate and inspect
docker compose logs --tail 100
docker compose logs -f web
docker compose exec redis redis-cli GET visits
docker compose top
docker compose images
docker compose stats --no-stream
5. Prove persistence and deploy a rebuild
docker compose down
docker compose up -d
curl http://127.0.0.1:8000/
docker compose build web
docker compose up -d --no-deps web
down removes containers and the default network but retains named volumes unless --volumes is supplied.
6. Stop or remove the lab
docker compose stop
docker compose start
docker compose down
# Destructive: remove the Redis data volume too
docker compose down --volumes
Use the final command only after deciding that the stored data is disposable or backed up.
References: Docker documentation and the Server World topic index.