Docker — Containerize from Day One

Multi-stage Dockerfile, docker-compose dev workflow with hot-reload, no virtualenv needed

10m 10m reading Lab included

The Problem

“Works on my machine” isn’t a deployment strategy. Virtual environments break, system Python versions conflict, and dependency installation differs between macOS and Linux. Containers solve this by making the environment reproducible.

Project Files

python-production-blueprint
Explorer
Loading…
Loading files from GitHub…

The Dockerfile

The full Dockerfile is in the project editor above. Here’s what each section does:

Line-by-Line Breakdown

Base image

FROM python:3.11-slim AS base

slim variant — no build tools, compilers, or man pages. 150MB vs 900MB for the full image.

Dependencies before code

COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY app/ app/

Docker layers are cached. By copying pyproject.toml first and installing dependencies, code changes don’t re-install packages. Only when dependencies change does this layer rebuild.

Log directory

RUN mkdir -p /var/log/app

Created inside the container for file-based logging. In Marathon or Swarm, the orchestrator mounts a host volume here so Vector can read the logs.

Non-root user

RUN adduser --disabled-password --gecos "" appuser && \
    chown -R appuser:appuser /var/log/app
USER appuser

Running as root inside containers is a security vulnerability. If an attacker escapes the container, they’re root on the host. USER appuser drops privileges.

Built-in health check

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

Docker and orchestrators use this to determine if the container is healthy. start-period gives the app 10 seconds to boot before health checks begin.

Docker Compose — Dev Workflow

The full docker-compose.yml is in the project editor above. Here are the key design decisions:

Key design decisions

No virtualenv needed. docker compose up builds the image with all dependencies. Your system Python is irrelevant.

Hot reload. The volume mount ./app:/app/app:ro maps your local source code into the container. --reload watches for changes. Edit a file, save, the server restarts inside the container.

Read-only mount. :ro prevents the container from writing to your source code directory.

Named volume for logs. app-logs persists log files across container restarts.

Development Workflow

# Start development
docker compose up

# Rebuild after dependency changes
docker compose up --build

# Run in background
docker compose up -d

# View logs
docker compose logs -f app

# Stop
docker compose down

Test the Container

docker compose up -d
curl http://localhost:8000/health
# {"status":"healthy","service":"python-production-blueprint","version":"0.2.0","environment":"staging"}

curl http://localhost:8000/docs
# Swagger UI

Next Step

Module 1 is complete. In Module 2, we design the API routes properly — RESTful design with FastAPI Router, error handling, and versioning.