Docker has transformed how Python applications are developed, tested, and deployed. By containerizing your applications, you eliminate "works on my machine" problems, simplify dependency management, and create reproducible environments that work identically everywhere.
This guide is designed for Python developers who want to get started with Docker, covering everything from basic concepts to production-ready configurations.
Why Docker for Python?
- Consistent environments: Development, testing, and production all use the same container
- Dependency isolation: No more virtual environment conflicts or system-level package issues
- Easy onboarding: New team members get a working environment with a single command
- Reproducible builds: The same Docker image always produces the same environment
- Simplified deployment: Deploy to any cloud platform that supports containers
Your First Python Dockerfile
# Use official Python image as base
FROM python:3.12-slim
# Set working directory
WORKDIR /app
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements first (for better caching)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Run the application
CMD ["python", "app.py"]
Building and Running
# Build the image
docker build -t my-python-app .
# Run the container
docker run -d --name my-app -p 8000:8000 my-python-app
# View logs
docker logs -f my-app
# Stop and remove
docker stop my-app && docker rm my-app
Multi-Stage Builds for Smaller Images
Production images should be as small as possible. Multi-stage builds let you separate build dependencies from runtime:
# Stage 1: Build
FROM python:3.12-slim AS builder
WORKDIR /build
ENV PYTHONDONTWRITEBYTECODE=1 PIP_NO_CACHE_DIR=1
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc g++ libffi-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --user -r requirements.txt
# Stage 2: Runtime
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PATH=/root/.local/bin:$PATH
# Copy only installed packages from builder
COPY --from=builder /root/.local /root/.local
# Copy application code
COPY . .
# Run as non-root user
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["python", "app.py"]
Docker Compose for Development
Docker Compose simplifies multi-container development environments:
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
volumes:
- .:/app # Live code reloading
- pip-cache:/root/.cache/pip
environment:
- DEBUG=true
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
volumes:
postgres-data:
redis-data:
pip-cache:
# Start all services
docker compose up -d
# View logs for all services
docker compose logs -f
# Rebuild and restart
docker compose up -d --build
# Stop everything
docker compose down
# Stop and remove volumes (clean slate)
docker compose down -v
Development vs Production Configurations
Development Dockerfile
FROM python:3.12-slim
WORKDIR /app
# Install dev tools
RUN pip install watchdog flake8 black pytest
# Copy everything and use volume mount for live reload
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Use watchdog for auto-reload
CMD ["watchmedo", "auto-restart", "--directory=/app", "--pattern=*.py", "--recursive", "--", "python", "app.py"]
Production Dockerfile
FROM python:3.12-slim AS builder
WORKDIR /build
RUN pip install --user -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=3s \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "--threads", "2", "app:app"]
Docker Compose Override for Production
# docker-compose.prod.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
restart: unless-stopped
environment:
- DEBUG=false
volumes: # No volume mount in production
- ./logs:/app/logs
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
# Run with production overrides
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
For the complete guide with all code examples and advanced patterns, read the full article on our blog.
Originally published at WD Tech Blog. Follow for more Python tutorials, AI tools, and developer resources.
Top comments (0)