Skip to content
VDS · Sites web et serveurs web

Une application Python : gunicorn et systemd

Exécuter Django, Flask ou FastAPI sur VDS : environnement virtuel, gunicorn avec un socket, service systemd et choix du nombre de workers selon votre palier.

Mis à jour le 23 août 2026

Le serveur de développement intégré (manage.py runserver, flask run) n'est pas conçu pour la charge de production. Sur un VPS, vous installez gunicorn ou uvicorn, et un serveur web les expose vers l'extérieur.

Environnement

N'installez jamais les paquets du projet dans le Python système — une mise à jour du système casserait l'application.

apt install -y python3 python3-venv python3-pip
useradd -r -m -d /srv/app -s /usr/sbin/nologin appuser

sudo -u appuser python3 -m venv /srv/app/venv
sudo -u appuser /srv/app/venv/bin/pip install -r /srv/app/requirements.txt
sudo -u appuser /srv/app/venv/bin/pip install gunicorn

Pour FastAPI, installez en plus uvicorn[standard] — gunicorn l'exécute comme classe de worker.

Service

/etc/systemd/system/myapp.service :

[Unit]
Description=Python app
After=network-online.target postgresql.service

[Service]
User=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/srv/app/venv/bin/gunicorn \
  --workers 3 \
  --bind unix:/run/myapp/gunicorn.sock \
  --access-logfile - \
  myproject.wsgi:application
RuntimeDirectory=myapp
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Pour FastAPI, remplacez la ligne de lancement par --worker-class uvicorn.workers.UvicornWorker app.main:app.

systemctl daemon-reload
systemctl enable --now myapp
journalctl -u myapp -f

RuntimeDirectory=myapp crée /run/myapp avec les permissions nécessaires à chaque démarrage — sinon il n'y a nulle part où placer le socket.

Combien de workers

La formule habituelle est 2 × cores + 1, mais elle se heurte aux limites de mémoire : chaque worker Django utilise 80–200 Mo.

OffreCœursWorkers
VDS-112
VDS-223
VDS-345
VDS-469

Si l'application passe beaucoup de temps à attendre le réseau et la base de données, ajoutez --threads 2 plutôt que d'augmenter le nombre de processus.

Serveur web

location / {
    proxy_pass http://unix:/run/myapp/gunicorn.sock;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

location /static/ { alias /srv/app/static/; }

Servez les fichiers statiques de Django directement avec Nginx après les avoir collectés : collectstatic. Les faire transiter par Python ajoute une charge inutile.

Erreurs courantes

SymptômeCause
502 et connect() to unix:... failed (13)Nginx n'a pas les permissions sur le socket — définissez --umask 007 et un groupe partagé
ModuleNotFoundErrorExécution avec le Python système au lieu de venv
DisallowedHost dans DjangoLe domaine n'est pas ajouté à ALLOWED_HOSTS
Les workers sont tués par timeoutUne requête longue — augmentez --timeout, déplacez le travail lourd vers une file d'attente
Tout s'est arrêté après une releaseVous avez oublié systemctl restart myapp — le code en mémoire ne se met pas à jour tout seul

Gardez les secrets dans un EnvironmentFile avec les permissions 600, pas dans settings.py. Un fichier de configuration finira dans le dépôt tôt ou tard.