Dans le chapitre précédent, on a vu Tests et documentation automatique. Maintenant, ton API fonctionne en local. Il est temps de la mettre en production.
🎯 Objectif : Conteneuriser ton API FastAPI avec Docker, la placer derrière Nginx, automatiser le déploiement avec un pipeline CI/CD, et appliquer les bonnes pratiques qui font la différence entre un projet perso et une API de production.
Conteneuriser avec Docker
Une API qui tourne sur ta machine ne sert personne. Docker garantit que ton environnement est identique partout — du laptop au serveur de prod. Plus de “ça marche chez moi”.
Le Dockerfile optimisé
La clé : séparer l’installation des dépendances du code source. Docker met en cache chaque couche. Si seul ton code change, les dépendances ne sont pas réinstallées — le build passe de 2 minutes à 5 secondes.
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /app
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", "--workers", "4"]
💡 Nombre de workers : la formule classique est (2 × CPU) + 1. Sur un serveur 2 cores, mets 5 workers. En production sérieuse, Gunicorn pilote les workers Uvicorn — pense à Gunicorn comme le manager d’une équipe de serveurs dans un restaurant.
Docker Compose : l’orchestration complète
Un fichier docker-compose.yml assemble tous les services : API, base de données, reverse proxy. Un seul docker compose up -d et tout démarre dans le bon ordre.
services:
api:
build: .
container_name: fastapi-app
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://fastapi:secret123@postgres:5432/app
- SECRET_KEY=${SECRET_KEY}
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
postgres:
image: postgres:16
environment:
POSTGRES_USER: fastapi
POSTGRES_PASSWORD: secret123
POSTGRES_DB: app
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U fastapi"]
interval: 5s
retries: 5
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- api
restart: unless-stopped
volumes:
postgres_data:
🔥 Le healthcheck sur PostgreSQL est crucial. Sans lui, ton API démarre avant que la base soit prête et plante au premier appel SQL. Le depends_on avec condition: service_healthy résout ce problème classique que tout le monde rencontre.
Nginx comme reverse proxy
Nginx se place devant ton API pour gérer le SSL, le rate limiting et les headers de sécurité. Uvicorn n’est pas conçu pour être exposé directement à Internet — il manque de protections essentielles contre les attaques courantes.
upstream fastapi {
server api:8000;
}
server {
listen 80;
server_name api.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header Strict-Transport-Security "max-age=31536000";
location / {
proxy_pass http://fastapi;
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;
}
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://fastapi;
}
}
⚠️ Le rate limiting protège ton API des abus. 10 requêtes par seconde par IP avec un burst de 20, c’est un bon point de départ. Ajuste selon ton trafic réel. Les headers de sécurité (X-Frame-Options, HSTS) bloquent les attaques les plus courantes — clickjacking, downgrade HTTPS, injection de contenu.
Configuration et secrets
Ne hardcode jamais un mot de passe ou une clé API. Utilise Pydantic Settings pour charger les variables d’environnement de façon typée et validée. Si une variable manque, l’API refuse de démarrer — mieux vaut un crash immédiat qu’une faille silencieuse.
# config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str
secret_key: str
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
env: str = "development"
class Config:
env_file = ".env"
settings = Settings()
Crée un fichier .env pour les valeurs locales, et un .env.example avec des valeurs fictives pour que l’équipe sache quoi configurer. Le .env ne va jamais dans Git — ajoute-le dans .gitignore dès la première minute.
💡 En production, les secrets viennent de variables d’environnement injectées par Docker, Kubernetes ou un vault (HashiCorp Vault, AWS Secrets Manager). Le fichier .env est uniquement pour le développement local.
Pipeline CI/CD avec GitHub Actions
L’automatisation élimine les erreurs humaines. À chaque push sur main, la pipeline teste, builde et déploie automatiquement. Un test qui échoue bloque tout — zéro compromis sur la qualité.
name: CI/CD FastAPI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test
ports: ["5432:5432"]
options: --health-cmd pg_isready --health-interval 10s --health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt pytest httpx ruff
- run: ruff check . && ruff format --check .
- run: pytest --cov=. -v
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test
SECRET_KEY: test-secret
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- uses: docker/build-push-action@v5
with:
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/fastapi-app:${{ github.sha }}
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/fastapi-app
docker compose pull && docker compose up -d
🎯 Le flux complet : push → lint avec ruff → tests pytest avec une vraie base PostgreSQL → build de l’image Docker → push sur le registry → déploiement SSH sur le serveur. Tout est automatique. Une PR qui casse les tests ne peut pas être mergée.
Pièges classiques et checklist
⚠️ Les erreurs qui coûtent cher en production :
uvicorn --reloaden prod — Le hot-reload consomme des ressources et provoque des comportements imprévisibles sous charge. C’est un outil de développement, point.allow_origins=["*"]dans CORS — Ça ouvre ton API à n’importe quel site web. Liste explicitement les domaines autorisés.- Pas de healthcheck — Sans endpoint
/health, ton orchestrateur ne sait pas si ton API est vivante. Docker, Kubernetes, ton load balancer — tous en ont besoin. - Swagger exposé en production — La doc auto est une mine d’or pour les attaquants. Désactive-la ou protège-la derrière une authentification.
Base.metadata.create_all()en prod — Utilise Alembic pour versionner les migrations de schéma. Chaque modification est tracée, réversible, et appliquée automatiquement au déploiement.- Logs non structurés — En prod, utilise
python-json-loggerpour des logs JSON compatibles avec ELK ou Grafana Loki. Lesprint()ne scalent pas.
🎯 Checklist avant mise en prod :
- ✅ Secrets dans
.envou un vault, jamais dans le code - ✅ HTTPS avec Let’s Encrypt configuré
- ✅ Rate limiting activé sur Nginx
- ✅ Migrations Alembic en place
- ✅ Tests > 80% de couverture, pipeline CI verte
- ✅ Logging structuré et centralisé
- ✅ Backups PostgreSQL automatisés
- ✅
.gitignorecomplet (.env,__pycache__, etc.)
Résumé
🔥 Ce qu’on a couvert : Docker pour conteneuriser l’API, Docker Compose pour orchestrer les services, Nginx comme reverse proxy avec SSL et rate limiting, Pydantic Settings pour la gestion des secrets, GitHub Actions pour le CI/CD automatique, et les pièges classiques de production à éviter absolument.
Avec cette série FastAPI en 6 chapitres, tu as tout pour construire une API professionnelle — des premiers endpoints jusqu’au déploiement automatisé. Le reste, c’est de la pratique : choisis un projet, code-le, déploie-le, itère. C’est comme ça qu’on progresse.
💡 À retenir : une API en production, c’est 20% de code métier et 80% d’infrastructure autour — tests, CI/CD, monitoring, sécurité. Ne néglige jamais cette partie. C’est elle qui fait la différence entre un prototype et un produit.
🖥️ Pratique sur ton propre serveur
Pour suivre Apprendre FastAPI en conditions réelles, tu as besoin d'un VPS. DigitalOcean offre 200$ de crédit gratuit pour démarrer.
Contenu réservé aux abonnés
Ce chapitre fait partie de la formation complète. Abonne-toi pour débloquer tous les contenus.
Débloquer pour 29 CHF/moisLe chapitre 1 de chaque formation est gratuit.
Série pas encore débloquée
Termine la série prérequise d'abord pour accéder à ce contenu.
Aller à la série prérequiseSur cette page
Articles liés
Tests et documentation automatique
Sécurise ton API avec JWT et OAuth2, écris des tests automatisés avec pytest, et génère la documentation OpenAPI/Swagger automatiquement.
FastAPI : ta première API en Python
Comprends les APIs, le protocole HTTP, l'architecture REST, puis installe FastAPI et crée ton premier endpoint. Le socle pour tout ce qui suit.
Routes, paramètres et validation
Path parameters, query parameters, validation des données avec Pydantic et gestion des headers HTTP dans FastAPI.