539 lines
12 KiB
Markdown
539 lines
12 KiB
Markdown
# 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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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`:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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)
|
|
|
|
```yaml
|
|
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
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```nginx
|
|
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
|
|
|
|
```yaml
|
|
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
|
|
|
|
```yaml
|
|
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
|
|
|
|
```dockerfile
|
|
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
|
|
|
|
```dockerfile
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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:**
|
|
```yaml
|
|
# 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):
|
|
```yaml
|
|
# 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:**
|
|
```yaml
|
|
# Remove or comment out for production
|
|
# volumes:
|
|
# - ./src/app:/code/app
|
|
# - ./src/.env:/code/.env
|
|
```
|
|
|
|
## Environment Configuration
|
|
|
|
### Service Communication
|
|
|
|
Services communicate using service names:
|
|
|
|
```yaml
|
|
# 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:**
|
|
```bash
|
|
# 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:**
|
|
```bash
|
|
# 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:**
|
|
```bash
|
|
# 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! |