Dockerizing Django and FastAPI Applications: A Complete Guide

Introduction

Docker has become the standard for containerizing applications, making deployment consistent across different environments. Whether you're working with Django or FastAPI, Dockerizing your application ensures it runs the same way on your local machine, CI/CD pipelines, and production servers.

🐳 Why Docker? Containerization solves the "it works on my machine" problem and simplifies deployment, scaling, and dependency management.

Prerequisites

Before we start, ensure you have:

  • Docker Desktop installed (download here)
  • Basic understanding of Django or FastAPI
  • A project ready to containerize

Dockerizing a Django Application

Step 1: Create a Dockerfile

Create a Dockerfile in your Django project root:

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

# Set working directory
WORKDIR /app

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

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

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

# Copy project
COPY . .

# Collect static files
RUN python manage.py collectstatic --noinput

# Expose port
EXPOSE 8000

# Run the application
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

Step 2: Create a .dockerignore File

Prevent unnecessary files from being copied into the image:

# .dockerignore
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
.venv
pip-log.txt
pip-delete-this-directory.txt
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.gitignore
.mypy_cache
.pytest_cache
.hypothesis
.DS_Store
*.db
*.sqlite3
.env
.env.local

Step 3: Create docker-compose.yml

For a complete setup with database and Redis:

version: '3.8'

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

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

  web:
    build: .
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    environment:
      - DEBUG=0
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/myproject
    depends_on:
      - db
      - redis

Step 4: Update Django Settings

Modify your settings.py to work with Docker:

import os
from pathlib import Path

# Build paths inside the project
BASE_DIR = Path(__file__).resolve().parent.parent

# Security settings for production
DEBUG = os.getenv('DEBUG', '0') == '1'
SECRET_KEY = os.getenv('SECRET_KEY', 'your-secret-key-here')

ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'localhost').split(',')

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.getenv('DB_NAME', 'myproject'),
        'USER': os.getenv('DB_USER', 'postgres'),
        'PASSWORD': os.getenv('DB_PASSWORD', 'postgres'),
        'HOST': os.getenv('DB_HOST', 'db'),
        'PORT': os.getenv('DB_PORT', '5432'),
    }
}

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

Dockerizing a FastAPI Application

Step 1: Create a Dockerfile for FastAPI

FROM python:3.11-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

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

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

# Copy application
COPY . .

# Expose port
EXPOSE 8000

# Run with Uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Step 2: Multi-stage Build for Production

Optimize your Docker image size with multi-stage builds:

# Build stage
FROM python:3.11-slim as builder

WORKDIR /app

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

# Install Python dependencies
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 . .

# Make sure scripts in .local are usable
ENV PATH=/root/.local/bin:$PATH

EXPOSE 8000

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

Step 3: FastAPI docker-compose.yml

version: '3.8'

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

  web:
    build: .
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
    volumes:
      - .:/app
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/fastapi_db
    depends_on:
      - db

Best Practices for Docker

1. Use Specific Base Image Tags

# ❌ Bad
FROM python:latest

# ✅ Good
FROM python:3.11-slim

2. Leverage Layer Caching

Order your Dockerfile commands from least to most frequently changing:

# Install dependencies first (changes less frequently)
COPY requirements.txt .
RUN pip install -r requirements.txt

# Copy code last (changes frequently)
COPY . .

3. Use .dockerignore

Always include a .dockerignore file to reduce build context size and speed up builds.

4. Run as Non-Root User

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

5. Health Checks

Add health checks to your Dockerfile:

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

Running Your Containerized Applications

Build and Run Django

# Build the image
docker build -t my-django-app .

# Run the container
docker run -p 8000:8000 my-django-app

# Or use docker-compose
docker-compose up --build

Build and Run FastAPI

# Build the image
docker build -t my-fastapi-app .

# Run the container
docker run -p 8000:8000 my-fastapi-app

# Or use docker-compose
docker-compose up --build

Debugging Docker Containers

View Logs

# View logs
docker-compose logs -f web

# View last 100 lines
docker-compose logs --tail=100 web

Execute Commands Inside Container

# Django
docker-compose exec web python manage.py migrate
docker-compose exec web python manage.py createsuperuser

# FastAPI
docker-compose exec web alembic upgrade head

Inspect Running Container

# Get shell access
docker-compose exec web bash

# Check environment variables
docker-compose exec web env

Production Considerations

1. Use Production WSGI/ASGI Servers

# Django - Use Gunicorn
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]

# FastAPI - Use Uvicorn with workers
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

2. Environment Variables

Never hardcode secrets. Use environment variables or secrets management:

# docker-compose.prod.yml
services:
  web:
    environment:
      - SECRET_KEY=${SECRET_KEY}
      - DATABASE_URL=${DATABASE_URL}
    env_file:
      - .env.production

3. Volume Management

For production, use named volumes for persistent data:

volumes:
  postgres_data:
  static_volume:
  media_volume:

Common Issues and Solutions

Issue: Database Connection Refused

Solution: Ensure the database service is ready before the web service starts:

services:
  web:
    depends_on:
      db:
        condition: service_healthy
    # ... rest of config

  db:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

Issue: Static Files Not Loading

Solution: Ensure static files are collected and served properly:

# settings.py
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATIC_URL = '/static/'

Issue: Slow Build Times

Solution: Use BuildKit and cache mounts:

# syntax=docker/dockerfile:1.4
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

Conclusion

Dockerizing your Django or FastAPI applications provides consistency, portability, and easier deployment. By following these best practices, you'll create production-ready containerized applications that are secure, efficient, and maintainable.

🚀 Next Steps: Consider deploying your containerized applications to platforms like AWS ECS, Google Cloud Run, or DigitalOcean App Platform.


Resources:

Related Articles:

STAY IN TOUCH

Get notified when I publish something new, and unsubscribe at any time.