Files
2025-10-19 22:09:35 +03:00

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!