Skip to content

Docker Setup

Learn how to configure and run the FastAPI Boilerplate using Docker Compose. The project includes a complete containerized setup with PostgreSQL, Redis, background workers, and optional services.

Docker Compose Architecture

The boilerplate includes these core services:

services:
  web:          # FastAPI application (uvicorn or gunicorn)
  worker:       # ARQ background task worker  
  db:           # PostgreSQL 13 database
  redis:        # Redis Alpine for caching/queues
  # Optional services (commented out by default):
  # pgadmin:    # Database administration
  # nginx:      # Reverse proxy
  # create_superuser: # One-time superuser creation
  # create_tier:      # One-time tier creation

Basic Docker Compose

Main Configuration

The main docker-compose.yml includes:

version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    # Development mode (reload enabled)
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
    # Production mode (uncomment for production)
    # command: gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000
    env_file:
      - ./src/.env
    ports:
      - "8000:8000"
    depends_on:
      - db
      - redis
    volumes:
      - ./src/app:/code/app
      - ./src/.env:/code/.env

  worker:
    build:
      context: .
      dockerfile: Dockerfile
    command: arq app.core.worker.settings.WorkerSettings
    env_file:
      - ./src/.env
    depends_on:
      - db
      - redis
    volumes:
      - ./src/app:/code/app
      - ./src/.env:/code/.env

  db:
    image: postgres:13
    env_file:
      - ./src/.env
    volumes:
      - postgres-data:/var/lib/postgresql/data
    expose:
      - "5432"

  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data
    expose:
      - "6379"

volumes:
  postgres-data:
  redis-data:

Environment File Loading

All services automatically load environment variables from ./src/.env:

env_file:
  - ./src/.env

The Docker services use these environment variables:

  • POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB for database
  • REDIS_*_HOST variables automatically resolve to service names
  • All application settings from your .env file

Service Details

Web Service (FastAPI Application)

The web service runs your FastAPI application:

web:
  build:
    context: .
    dockerfile: Dockerfile
  # Development: uvicorn with reload
  command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
  # Production: gunicorn with multiple workers (commented out)
  # command: gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000
  env_file:
    - ./src/.env
  ports:
    - "8000:8000"  # Direct access in development
  volumes:
    - ./src/app:/code/app  # Live code reloading
    - ./src/.env:/code/.env

Key Features:

  • Development mode: Uses uvicorn with --reload for automatic code reloading
  • Production mode: Switch to gunicorn with multiple workers (commented out)
  • Live reloading: Source code mounted as volume for development
  • Port exposure: Direct access on port 8000 (can be disabled for nginx)

Worker Service (Background Tasks)

Handles background job processing with ARQ:

worker:
  build:
    context: .
    dockerfile: Dockerfile
  command: arq app.core.worker.settings.WorkerSettings
  env_file:
    - ./src/.env
  depends_on:
    - db
    - redis
  volumes:
    - ./src/app:/code/app
    - ./src/.env:/code/.env

Features: - Runs ARQ worker for background job processing - Shares the same codebase and environment as web service - Automatically connects to Redis for job queues - Live code reloading in development

Database Service (PostgreSQL 13)

db:
  image: postgres:13
  env_file:
    - ./src/.env
  volumes:
    - postgres-data:/var/lib/postgresql/data
  expose:
    - "5432"  # Internal network only

Configuration: - Uses environment variables: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB - Data persisted in named volume postgres-data - Only exposed to internal Docker network (no external port) - To enable external access, uncomment the ports section

Redis Service

redis:
  image: redis:alpine
  volumes:
    - redis-data:/data
  expose:
    - "6379"  # Internal network only

Features: - Lightweight Alpine Linux image - Data persistence with named volume - Used for caching, job queues, and rate limiting - Internal network access only

Optional Services

Database Administration (pgAdmin)

Uncomment to enable web-based database management:

pgadmin:
  container_name: pgadmin4
  image: dpage/pgadmin4:latest
  restart: always
  ports:
    - "5050:80"
  volumes:
    - pgadmin-data:/var/lib/pgadmin
  env_file:
    - ./src/.env
  depends_on:
    - db

Usage: - Access at http://localhost:5050 - Requires PGADMIN_DEFAULT_EMAIL and PGADMIN_DEFAULT_PASSWORD in .env - Connect to database using service name db and port 5432

Reverse Proxy (Nginx)

Uncomment for production-style reverse proxy:

nginx:
  image: nginx:latest
  ports:
    - "80:80"
  volumes:
    - ./default.conf:/etc/nginx/conf.d/default.conf
  depends_on:
    - web

Configuration: The included default.conf provides:

server {
    listen 80;

    location / {
        proxy_pass http://web:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

When using nginx:

  1. Uncomment the nginx service
  2. Comment out the ports section in the web service
  3. Uncomment expose: ["8000"] in the web service

Initialization Services

Create First Superuser

create_superuser:
  build:
    context: .
    dockerfile: Dockerfile
  env_file:
    - ./src/.env
  depends_on:
    - db
    - web
  command: python -m src.scripts.create_first_superuser
  volumes:
    - ./src:/code/src

Create First Tier

create_tier:
  build:
    context: .
    dockerfile: Dockerfile
  env_file:
    - ./src/.env
  depends_on:
    - db
    - web
  command: python -m src.scripts.create_first_tier
  volumes:
    - ./src:/code/src

Usage:

  • These are one-time setup services
  • Uncomment when you need to initialize data
  • Run once, then comment out again

Dockerfile Details

The project uses a multi-stage Dockerfile with uv for fast Python package management:

Builder Stage

FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS builder

ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy

WORKDIR /app

# Install dependencies (cached layer)
RUN --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv sync --locked --no-install-project

# Copy and install project
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --locked --no-editable

Final Stage

FROM python:3.11-slim-bookworm

# Create non-root user for security
RUN groupadd --gid 1000 app \
    && useradd --uid 1000 --gid app --shell /bin/bash --create-home app

# Copy virtual environment from builder
COPY --from=builder --chown=app:app /app/.venv /app/.venv

ENV PATH="/app/.venv/bin:$PATH"
USER app
WORKDIR /code

# Default command (can be overridden)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

Security Features:

  • Non-root user execution
  • Multi-stage build for smaller final image
  • Cached dependency installation

Common Docker Commands

Development Workflow

# Start all services
docker compose up

# Start in background
docker compose up -d

# Rebuild and start (after code changes)
docker compose up --build

# View logs
docker compose logs -f web
docker compose logs -f worker

# Stop services
docker compose down

# Stop and remove volumes (reset data)
docker compose down -v

Service Management

# Start specific services
docker compose up web db redis

# Scale workers
docker compose up --scale worker=3

# Execute commands in running containers
docker compose exec web bash
docker compose exec db psql -U postgres
docker compose exec redis redis-cli

# View service status
docker compose ps

Production Mode

To switch to production mode:

  1. Enable Gunicorn:

    # Comment out uvicorn line
    # command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
    # Uncomment gunicorn line
    command: gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000
    

  2. Enable Nginx (optional):

    # Uncomment nginx service
    nginx:
      image: nginx:latest
      ports:
        - "80:80"
    
    # In web service, comment out ports and uncomment expose
    # ports:
    #   - "8000:8000"
    expose:
      - "8000"
    

  3. Remove development volumes:

    # Remove or comment out for production
    # volumes:
    #   - ./src/app:/code/app
    #   - ./src/.env:/code/.env
    

Environment Configuration

Service Communication

Services communicate using service names:

# In your .env file for Docker
POSTGRES_SERVER=db      # Not localhost
REDIS_CACHE_HOST=redis  # Not localhost
REDIS_QUEUE_HOST=redis
REDIS_RATE_LIMIT_HOST=redis

Port Management

Development (default): - Web: localhost:8000 (direct access) - Database: localhost:5432 (uncomment ports to enable) - Redis: localhost:6379 (uncomment ports to enable) - pgAdmin: localhost:5050 (if enabled)

Production with Nginx: - Web: localhost:80 (through nginx) - Database: Internal only - Redis: Internal only

Troubleshooting

Common Issues

Container won't start:

# Check logs
docker compose logs web

# Rebuild image
docker compose build --no-cache web

# Check environment file
docker compose exec web env | grep POSTGRES

Database connection issues:

# Check if db service is running
docker compose ps db

# Test connection from web container
docker compose exec web ping db

# Check database logs
docker compose logs db

Port conflicts:

# Check what's using the port
lsof -i :8000

# Use different ports
ports:
  - "8001:8000"  # Use port 8001 instead

Development vs Production

Development features:

  • Live code reloading with volume mounts
  • Direct port access
  • uvicorn with --reload
  • Exposed database/redis ports for debugging

Production optimizations:

  • No volume mounts (code baked into image)
  • Nginx reverse proxy
  • Gunicorn with multiple workers
  • Internal service networking only
  • Resource limits and health checks

Best Practices

Development

  • Use volume mounts for live code reloading
  • Enable direct port access for debugging
  • Use uvicorn with reload for fast development
  • Enable optional services (pgAdmin) as needed

Production

  • Switch to gunicorn with multiple workers
  • Use nginx for reverse proxy and load balancing
  • Remove volume mounts and bake code into images
  • Use internal networking only
  • Set resource limits and health checks

Security

  • Containers run as non-root user
  • Use internal networking for service communication
  • Don't expose database/redis ports externally
  • Use Docker secrets for sensitive data in production

Monitoring

  • Use docker compose logs to monitor services
  • Set up health checks for all services
  • Monitor resource usage with docker stats
  • Use structured logging for better observability

The Docker setup provides everything you need for both development and production. Start with the default configuration and customize as your needs grow!

Morty Proxy This is a proxified and sanitized view of the page, visit original site.