Intermédiaire ⏱ 25 min de mise en œuvre Mis à jour le 11 août 2026

Déployer une application Python (FastAPI) en production sur un VPS

Uvicorn avec --reload, c'est pour développer. En production, il faut plusieurs workers ASGI, un reverse proxy et de la supervision. Voici comment déployer FastAPI proprement sur un VPS.

FastAPI est un framework Python moderne conçu pour créer des API rapides, typées et documentées automatiquement. Il convient aussi bien à un backend web qu’à un service de traitement de données ou d’inférence pour une application d’IA.

En développement, la commande uvicorn main:app --reload suffit. En production, le rechargement automatique doit disparaître et un seul processus Uvicorn ne permet pas d’exploiter plusieurs coeurs ni d’assurer une disponibilité satisfaisante. Une architecture robuste associe plusieurs workers ASGI, un reverse proxy HTTPS et une supervision adaptée.

Ce guide présente un déploiement Docker, recommandé pour sa reproductibilité, ainsi qu’une alternative avec un environnement virtuel Python et systemd. Les futurs VPS TalCloud, hébergés en France sur du stockage NVMe, fourniront un socle adapté à ce type d’API, avec conformité RGPD, protection Anti-DDoS Netrix et support humain. La démarche est proche de notre guide pour déployer une application Node.js, adaptée à l’écosystème Python.

Prérequis pour déployer FastAPI

Il faut disposer des éléments suivants :

  • Un VPS Linux, par exemple sous Debian ou Ubuntu
  • Un nom de domaine pointant vers l’adresse IP du serveur
  • Docker et le plugin Docker Compose (voir installer Docker sur un VPS)
  • Une application FastAPI fonctionnelle
  • Les ports 80 et 443 ouverts dans le pare-feu
  • Un accès SSH avec un utilisateur autorisé à administrer Docker

Dans la suite, l’application écoute sur le port 8000 à l’intérieur du conteneur. Ce port ne sera pas exposé publiquement : seul le reverse proxy pourra y accéder.

Structurer le projet FastAPI

Une structure minimale peut ressembler à ceci :

fastapi-app/
├── main.py
├── requirements.txt
├── Dockerfile
├── compose.yaml
├── .dockerignore
└── .env

Le fichier main.py contient l’application et un endpoint de contrôle :

from fastapi import FastAPI

app = FastAPI(
    title="API TalCloud",
    version="1.0.0",
)

@app.get("/")
async def root():
    return {"message": "API opérationnelle"}

@app.get("/health")
async def health():
    return {"status": "ok"}

Le fichier requirements.txt définit des versions précises afin de rendre les builds reproductibles :

fastapi==0.116.1
uvicorn[standard]==0.35.0
gunicorn==23.0.0
pydantic-settings==2.10.1

Ajoutez aussi un fichier .dockerignore :

.git
.gitignore
.env
__pycache__
*.pyc
.venv
venv
tests

Le fichier .env doit être exclu du dépôt Git. Il accueillera les paramètres propres à chaque environnement, jamais les valeurs destinées à être publiées.

Choisir un serveur ASGI de production

FastAPI repose sur ASGI. Uvicorn est donc le serveur naturel pour l’exécuter. En production, plusieurs workers permettent de traiter plusieurs requêtes CPU ou tâches concurrentes dans des processus séparés :

uvicorn main:app \
  --host 0.0.0.0 \
  --port 8000 \
  --workers 4 \
  --proxy-headers

Une autre approche courante consiste à utiliser Gunicorn comme gestionnaire de processus avec des workers Uvicorn :

gunicorn main:app \
  --bind 0.0.0.0:8000 \
  --workers 4 \
  --worker-class uvicorn.workers.UvicornWorker \
  --timeout 120 \
  --access-logfile - \
  --error-logfile -

La classe uvicorn.workers.UvicornWorker reste fréquente dans les déploiements existants. Pour un nouveau projet, vérifiez sa compatibilité avec les versions installées, car l’écosystème évolue vers le paquet dédié uvicorn-worker.

Multiplier les workers améliore l’utilisation des coeurs disponibles, mais augmente aussi la consommation mémoire. Une base raisonnable consiste à commencer avec un ou deux workers par coeur, puis à ajuster après des tests de charge. Pour une tâche d’IA lourde, une file de travaux et des workers spécialisés sont souvent préférables à un timeout HTTP très élevé.

Conteneuriser FastAPI avec Docker

Voici un Dockerfile basé sur Python 3.12 slim :

FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1

WORKDIR /app

RUN addgroup --system appgroup \
    && adduser --system --ingroup appgroup appuser

COPY requirements.txt .
RUN pip install --upgrade pip \
    && pip install -r requirements.txt

COPY --chown=appuser:appgroup . .

USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)"

CMD ["gunicorn", "main:app", "--bind", "0.0.0.0:8000", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--timeout", "120", "--access-logfile", "-", "--error-logfile", "-"]

L’utilisateur non-root réduit l’impact d’une éventuelle compromission du processus. Le healthcheck permet à Docker et aux outils de supervision de détecter un service qui ne répond plus.

Construisez et testez l’image :

docker build -t fastapi-app:1.0.0 .
docker run --rm -p 127.0.0.1:8000:8000 fastapi-app:1.0.0

Vérifiez ensuite l’endpoint :

curl http://127.0.0.1:8000/health

Orchestrer le service avec Docker Compose

Le fichier compose.yaml simplifie le démarrage et la configuration :

services:
  app:
    build:
      context: .
    image: fastapi-app:1.0.0
    restart: unless-stopped
    env_file:
      - .env
    ports:
      - "127.0.0.1:8000:8000"
    healthcheck:
      test:
        - CMD
        - python
        - -c
        - "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)"
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

La liaison à 127.0.0.1 empêche un accès direct au port 8000 depuis Internet. Le trafic public doit obligatoirement passer par le reverse proxy.

Une application complète peut ajouter PostgreSQL et Redis au même fichier Compose. PostgreSQL stocke les données persistantes, tandis que Redis peut servir de cache, de broker ou de support pour une file de tâches. Ces services doivent rester sur un réseau Docker interne, sans port public sauf besoin explicite.

Démarrez l’ensemble en arrière-plan :

docker compose up -d --build
docker compose ps
docker compose logs -f app

Placer un reverse proxy HTTPS devant FastAPI

Nginx, Caddy, Traefik ou Nginx Proxy Manager peuvent terminer la connexion TLS et transmettre les requêtes vers FastAPI. Caddy automatise facilement les certificats, tandis que Nginx offre une configuration très explicite.

Exemple Nginx simplifié :

server {
    listen 80;
    server_name api.exemple.fr;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;

        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;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_connect_timeout 10s;
        proxy_read_timeout 120s;
    }
}

Il faut ensuite activer HTTPS avec un certificat TLS valide et rediriger le trafic HTTP vers HTTPS. Les en-têtes X-Forwarded-* permettent à FastAPI de connaître le protocole et l’adresse d’origine. Les en-têtes Upgrade et Connection sont nécessaires si l’application utilise des WebSockets.

N’exposez pas l’interface d’administration d’un reverse proxy sans authentification forte et filtrage réseau.

Gérer les variables d’environnement et les secrets

Une configuration ne doit pas être codée directement dans main.py. Utilisez un fichier .env non versionné ou, mieux encore, un gestionnaire de secrets fourni par l’environnement de déploiement.

Exemple avec pydantic-settings :

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    database_url: str
    redis_url: str
    secret_key: str
    environment: str = "production"

    model_config = SettingsConfigDict(
        env_file=".env",
        extra="ignore",
    )

settings = Settings()

Le fichier .env peut contenir :

DATABASE_URL=postgresql://app:mot-de-passe@db:5432/app
REDIS_URL=redis://redis:6379/0
SECRET_KEY=valeur-longue-et-aleatoire
ENVIRONMENT=production

Limitez les permissions de ce fichier et renouvelez les secrets compromis. Ne placez jamais une clé d’API, un mot de passe PostgreSQL ou un secret JWT dans l’image Docker.

Appliquer les bonnes pratiques de production

Ajustez le nombre de workers selon le nombre de coeurs, la mémoire et le comportement réel de l’application. Les endpoints asynchrones sont efficaces pour les entrées-sorties, mais un calcul CPU intensif bloque toujours le worker qui l’exécute.

Configurez des timeouts cohérents entre Gunicorn, le reverse proxy et les clients. Produisez des logs structurés sur la sortie standard afin que Docker ou une plateforme de supervision puisse les collecter. Évitez les données personnelles et les secrets dans les journaux.

L’endpoint /health doit rester rapide. Un second endpoint de disponibilité peut vérifier PostgreSQL, Redis et les dépendances indispensables. Restreignez CORS aux domaines autorisés au lieu d’utiliser * en production.

Les migrations de base de données doivent être exécutées avec prudence. Une commande dédiée avant le déploiement est généralement plus sûre que des migrations lancées simultanément par chaque worker.

Mettre à jour et superviser l’application

Versionnez chaque image avec un tag immuable, puis reconstruisez et redémarrez le service :

docker build -t fastapi-app:1.0.1 .
docker compose up -d
docker compose logs --tail=100 app

Conservez l’image précédente pour faciliter un retour arrière. Surveillez au minimum l’utilisation CPU, la mémoire, l’espace disque, les erreurs HTTP, la latence, les redémarrages et l’état du healthcheck. Testez également la restauration des sauvegardes PostgreSQL.

Sans Docker, une installation avec venv, Gunicorn et une unité systemd reste possible. systemd doit lancer le service sous un utilisateur dédié, charger les variables depuis un fichier protégé et redémarrer le processus en cas d’échec. Cette méthode est légère, mais demande davantage de discipline pour reproduire les versions et les déploiements.

Avec plusieurs workers ASGI, une image versionnée, un port interne, un reverse proxy HTTPS et une supervision active, une API FastAPI dispose d’une base solide pour la production. Les VPS TalCloud permettent d’héberger cette architecture en France sur NVMe, sans dépendance à un GPU, avec protection Anti-DDoS Netrix et accompagnement humain.