12 KiB
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_DBfor databaseREDIS_*_HOSTvariables automatically resolve to service names- All application settings from your
.envfile
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
--reloadfor 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_EMAILandPGADMIN_DEFAULT_PASSWORDin.env - Connect to database using service name
dband port5432
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:
- Uncomment the nginx service
- Comment out the
portssection in the web service - 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:
-
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 -
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" -
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 logsto 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!