Skip to main content

Docker and Containerization: Deploying Python Applications

Created: December 17, 2025 5 min read

Docker enables packaging Python applications with all dependencies into containers, ensuring consistency across development, testing, and production environments. See Python Guide for more context. See Python Guide for more context. See Python Guide for more context.

Docker Fundamentals

Installation

# Install Docker
# macOS: brew install docker
# Ubuntu: sudo apt-get install docker.io
# Windows: Download Docker Desktop

# Verify installation
docker --version
docker run hello-world

Basic Concepts

  • Image: Blueprint for containers (like a class)
  • Container: Running instance of an image (like an object)
  • Dockerfile: Instructions to build an image
  • Registry: Repository for storing images (Docker Hub, etc.)

Creating Docker Images

Simple Dockerfile

# Use official Python runtime as base image
FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Copy requirements
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose port
EXPOSE 8000

# Set environment variables
ENV PYTHONUNBUFFERED=1

# Run application
CMD ["python", "app.py"]

Multi-stage Dockerfile

# Build stage
FROM python:3.11 as builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Runtime stage
FROM python:3.11-slim

WORKDIR /app

# Copy only necessary files from builder
COPY --from=builder /root/.local /root/.local
COPY . .

ENV PATH=/root/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1

EXPOSE 8000
CMD ["python", "app.py"]

Dockerfile for Flask Application

FROM python:3.11-slim

WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# Copy and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Create non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 5000

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD python -c "import requests; requests.get('http://localhost:5000/health')"

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]

requirements.txt

Flask==2.3.0
requests==2.31.0
python-dotenv==1.0.0
gunicorn==21.0.0

Building and Running Containers

Build Image

# Build image
docker build -t my-python-app:1.0 .

# Build with build arguments
docker build -t my-app:latest --build-arg PYTHON_VERSION=3.11 .

# List images
docker images

# Remove image
docker rmi my-python-app:1.0

Run Container

# Run container
docker run -d --name my-container my-python-app:1.0

# Run with port mapping
docker run -d -p 8000:8000 --name my-app my-python-app:1.0

# Run with environment variables
docker run -d -e DATABASE_URL=postgres://... my-python-app:1.0

# Run with volume mount
docker run -d -v /host/path:/container/path my-python-app:1.0

# Run with resource limits
docker run -d --memory=512m --cpus=1 my-python-app:1.0

# Run interactively
docker run -it my-python-app:1.0 /bin/bash

# List running containers
docker ps

# Stop container
docker stop my-container

# Remove container
docker rm my-container

Docker Compose

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    volumes:
      - .:/app
    command: python app.py

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  celery:
    build: .
    command: celery -A tasks worker --loglevel=info
    environment:
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

volumes:
  postgres_data:

Docker Compose Commands

# Start services
docker-compose up

# Start in background
docker-compose up -d

# Stop services
docker-compose down

# View logs
docker-compose logs -f web

# Execute command in service
docker-compose exec web python manage.py migrate

# Rebuild images
docker-compose build

# Scale service
docker-compose up -d --scale celery=3

Best Practices

Optimize Image Size

# Bad: Large image
FROM python:3.11
RUN apt-get update && apt-get install -y build-essential
COPY requirements.txt .
RUN pip install -r requirements.txt

# Good: Smaller image
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

Security

# Create non-root user
RUN useradd -m -u 1000 appuser
USER appuser

# Use specific versions
FROM python:3.11.0-slim

# Don't run as root
# Don't include secrets in image
# Use .dockerignore to exclude unnecessary files

.dockerignore

__pycache__
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
.git
.gitignore
.dockerignore
.env
.env.local
*.log
.pytest_cache
.coverage
dist/
build/
*.egg-info/

Debugging Containers

# View container logs
docker logs my-container
docker logs -f my-container  # Follow logs

# Inspect container
docker inspect my-container

# Execute command in running container
docker exec -it my-container /bin/bash

# View resource usage
docker stats

# View processes in container
docker top my-container

# Copy files from container
docker cp my-container:/app/file.txt ./file.txt

Registry and Deployment

Push to Docker Hub

# Login to Docker Hub
docker login

# Tag image
docker tag my-python-app:1.0 username/my-python-app:1.0

# Push image
docker push username/my-python-app:1.0

# Pull image
docker pull username/my-python-app:1.0

Private Registry

# Tag for private registry
docker tag my-app:1.0 registry.example.com/my-app:1.0

# Push to private registry
docker push registry.example.com/my-app:1.0

# Pull from private registry
docker pull registry.example.com/my-app:1.0

Common Pitfalls

Bad Practice:

# Don't: Large base image
FROM python:3.11

# Don't: Cache pip packages
RUN pip install -r requirements.txt

# Don't: Run as root
# (no USER directive)

# Don't: Copy entire directory
COPY . .

Good Practice:

# Do: Use slim image
FROM python:3.11-slim

# Do: Don't cache pip
RUN pip install --no-cache-dir -r requirements.txt

# Do: Run as non-root
RUN useradd -m appuser
USER appuser

# Do: Use .dockerignore
COPY app/ /app/

Conclusion

Docker enables consistent, reproducible deployments of Python applications. Master Dockerfile creation, Docker Compose for multi-container applications, and best practices for security and efficiency. Containerization is essential for modern application deployment.

Resources

Comments

Share this article

Scan to read on mobile