Skip to content

Docker & Compose Proficient

🚀 Deployment · Level 4
⏱️ ~4 days

When you'd use this

Dockerfiles, multi-stage builds, docker-compose and container best practices.

Package an app and its dependencies into a reproducible container that runs the same everywhere.

Dockerfile for Python apps

A multi-stage build that produces a small, reproducible image for a Python service.

# ─── Multi-stage build (smaller final image) ─────
FROM python:3.13-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.13-slim
WORKDIR /app
COPY --from=builder /install /usr/local
COPY src/ ./src/
COPY pyproject.toml .

# Non-root user (security best practice)
RUN useradd -m appuser
USER appuser

EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose (local dev environment)

Spin up your app plus its dependencies (DB, cache) with one command for local dev.

# docker-compose.yml
services:
  app:
    build: .
    ports: ["8000:8000"]
    environment:
      DATABASE_URL: postgresql://postgres:secret@db:5432/myapp
      REDIS_URL: redis://cache:6379
    depends_on:
      db: { condition: service_healthy }
      cache: { condition: service_started }
    volumes:
      - ./src:/app/src   # hot reload in dev

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

  cache:
    image: redis:7-alpine
    ports: ["6379:6379"]

volumes:
  pgdata:
docker compose up -d        # start all services
docker compose logs -f app  # follow app logs
docker compose down         # stop and remove
docker compose exec app bash  # shell into container

Best practices

Slim images, non-root users, pinned deps, and .dockerignore for safe, lean containers.

Docker for Python

  • Use python:3.13-slim (not full image — 5x smaller)
  • Use multi-stage builds (separate build deps from runtime)
  • Pin dependency versions (reproducible builds)
  • Run as non-root user
  • Add .dockerignore (exclude .git, __pycache__, .venv, tests/)
  • Use HEALTHCHECK for orchestrators to monitor

Practice Exercises

  1. Dockerize a FastAPI app with multi-stage build, non-root user and health check.
  2. Create docker-compose for app + PostgreSQL + Redis with proper health checks.
  3. Optimize image size — compare full vs slim vs alpine, measure with docker images.
  4. Add hot reload for development using volume mounts.
  5. Build a CI pipeline that builds Docker image and pushes to Docker Hub.

💬 Discussion

Have a question about this topic? Found an error? Share your thoughts below.