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
:
The Docker services use these environment variables:
POSTGRES_USER
,POSTGRES_PASSWORD
,POSTGRES_DB
for databaseREDIS_*_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¶
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:
- Uncomment the nginx service
- Comment out the
ports
section 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:
-
Enable Nginx (optional):
-
Remove development volumes:
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!