Ever struggled with the classic "it works on my machine" problem? Or found yourself manually killing terminals instead of gracefully shutting down your FastAPI servers? Let's dive into a complete Docker setup that will transform your development workflow and prepare you for production!
π The Problem We're Solving
Picture this: You've built an amazing FastAPI recruitment portal with PostgreSQL, JWT authentication, and AI-powered features. Your code works perfectly locally, but:
Setting up the environment is complex for new team members
Database connections get messy when you kill terminals
Production deployment feels like a mystery
Environment variables are scattered everywhere
Enter Docker - your development superhero! π¦ΈββοΈ
# docker-compose.yml
services:
web: # Your FastAPI app
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY=${SECRET_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY}
depends_on:
- db
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
db: # PostgreSQL database
image: postgres:15
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data: # Persistent database storage
ποΈ The Complete Docker Architecture
Here's what we're building - a multi-container application that Just Works:
π§ The Magic Dockerfile
FROM python:3.12-slim
# Essential for Python in containers
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Install system dependencies for PostgreSQL
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
postgresql-client gcc python3-dev libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
π Security First: The Template Approach
Never commit secrets to Git! Here's the pro way to handle sensitive data:
What goes in Git:
# docker-compose.template.yml (safe to commit)
services:
web:
environment:
- DATABASE_URL=${DATABASE_URL}
- SECRET_KEY=${SECRET_KEY}
# Template shows structure, not actual values
What stays local:
# .env (NEVER commit this)
DATABASE_URL=postgresql://user:pass@localhost/db
SECRET_KEY=super-secret-jwt-key
OPENAI_API_KEY=sk-your-actual-key
Your .gitignore:
# Ignore the real compose file with secrets
docker-compose.yml
.env
.env.*
# But allow the template
!docker-compose.template.yml
!.env.example
β‘ The Developer Experience Revolution
Before Docker:
# The painful way π€
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Start PostgreSQL somehow...
# Set 15 environment variables...
# Kill terminal when done (bad!)
After Docker:
# The magical way β¨
docker compose up --build
# Everything just works!
# Graceful shutdown
docker compose down
π οΈ Essential Docker Commands Every Developer Needs
# Development workflow
docker compose up --build # Start with rebuild
docker compose up -d # Background mode
docker compose logs -f web # Follow logs
docker compose down # Graceful shutdown
# Database operations
docker compose exec db psql -U postgres -d recruitment_db
docker compose down -v # Fresh start (deletes data)
# Debugging
docker compose ps # See running services
docker compose exec web bash # Shell into container
π¨Common Gotchas & Solutions
Permission Denied Error?
# Add yourself to docker group (one-time setup)
sudo usermod -aG docker $USER
newgrp docker
# Now docker commands work without sudo!
Database Connection Issues?
# Check if database is ready
curl http://localhost:8000/db-check
# Should return: {"database": "connected", "result": 1}
Port Already in Use?
# Find what's using port 8000
sudo lsof -i :8000
# Kill it or change ports in docker-compose.yml
π Production-Ready Deployment
Transform your development setup to production:
# docker-compose.prod.yml
services:
web:
command: uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
restart: unless-stopped
# Remove --reload flag for production
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
depends_on:
- web
# Load balancer + SSL termination
π‘ Pro Tips for FastAPI + Docker
- Graceful Shutdown: Your FastAPI app can run cleanup code:
@app.on_event("shutdown")
async def shutdown_event():
"""Cleanup database connections"""
cleanup()
- Health Checks: Always include health endpoints:
@app.get("/health")
def health_check():
return {"status": "healthy"}
@app.get("/db-check")
def db_check():
# Test database connectivity
pass
- Environment-Specific Configs: Use different compose files:
# Development
docker compose up
# Production
docker compose -f docker-compose.prod.yml up
π― The Bottom Line
Docker transforms your FastAPI development from chaotic to professional:
β
Consistent environments across all machines
β
One-command setup for new developers
β
Graceful shutdowns preserve data integrity
β
Production-ready deployment pipeline
β
Secure secret management with templates
β
Database included - no manual setup needed
π Ready to Dockerize Your FastAPI App?
Start with these files in your project:
Dockerfile
- Your app container
docker-compose.template.yml
- Safe template for Git
.env.example
- Environment variable examples
README-Docker.md
- Setup instructions
Updated .gitignore
- Security first!
Your future self (and your team) will thank you for the professional setup!
Top comments (0)