Ouverture commerciale reportée le temps de finaliser nos locaux à Limoges. Être prévenu de l'ouverture

Déployer une application Python (Flask ou FastAPI)

Bots et applicatif · 5 min de lecture · Mis à jour le 16 octobre 2026

flask run et fastapi dev sont faits pour développer, pas pour servir des visiteurs. En production, une application Python tourne derrière un serveur d'application : Gunicorn pour Flask (WSGI), Uvicorn pour FastAPI (ASGI). Ce guide prépare le dépôt, puis le publie sur l'hébergement applicatif HebergHub ou sur un VPS.

Préparer le dépôt

Structure

mon-app/
├── app.py            # ou main.py pour FastAPI
├── requirements.txt
├── .gitignore
└── .env.example

Flask avec Gunicorn

# app.py
import os
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix

app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]

# Le HTTPS est géré en amont : lire X-Forwarded-Proto et X-Forwarded-For
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1)

@app.get("/")
def accueil():
    return "Bonjour depuis HebergHub"

@app.get("/sante")
def sante():
    return {"ok": True}
# requirements.txt
flask==3.1.*
gunicorn==23.*

Commande de démarrage :

gunicorn --workers 2 --bind 0.0.0.0:${PORT:-8000} app:app

FastAPI avec Uvicorn

# main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def accueil():
    return {"message": "Bonjour depuis HebergHub"}

@app.get("/sante")
def sante():
    return {"ok": True}
# requirements.txt
fastapi==0.115.*
uvicorn[standard]==0.32.*

Commande de démarrage :

uvicorn main:app --host 0.0.0.0 --port ${PORT:-8000} --proxy-headers --forwarded-allow-ips='*'

--proxy-headers fait confiance à l'en-tête X-Forwarded-Proto envoyé par le relais HTTPS ; sans lui, les URL générées par l'application commencent par http://.

Dans les deux cas :

Option 1 : l'hébergement applicatif

  1. Commandez la formule Application Starter (1,99 € TTC par mois, jusqu'à 2 Go de RAM et 2 vCPU). Le seul champ demandé est le nom du projet.
  2. Dans votre espace client, ouvrez le service puis « Nouvelle application » : nom, sous-domaine en .apps.heberghub.fr, Dépôt public (adresse https) ou Mes dépôts GitHub (privés), branche, type de build, port de l'application (8000 dans les exemples), variables d'environnement (une par ligne, NOM=valeur, par exemple SECRET_KEY=…).
  3. Dans Options avancées, imposez la commande de démarrage : la ligne gunicorn … ou uvicorn … ci-dessus. Sans elle, la détection automatique peut lancer un serveur de développement ou ne rien trouver.
  4. Cliquez sur « Déployer » : 5 à 15 minutes pour le premier déploiement, beaucoup moins ensuite.
  5. Ouvrez « Journaux » : Gunicorn affiche Listening at: http://0.0.0.0:8000, Uvicorn Uvicorn running on http://0.0.0.0:8000.

Si l'application plante, elle est relancée automatiquement. « Redémarrer », « Arrêter » et « Redéployer » sont sur sa fiche ; l'encadré « Pourquoi ça a échoué » résume la cause d'un déploiement raté. Les variables modifiées (bouton « Variables », « Enregistrer ») s'appliquent au prochain redéploiement.

Avec un Dockerfile

Le type de build Dockerfile ne laisse rien au hasard :

FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
# Flask :
CMD ["gunicorn", "--workers", "2", "--bind", "0.0.0.0:8000", "app:app"]
# FastAPI : CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips", "*"]

PYTHONUNBUFFERED=1 fait apparaître les messages dans les journaux dès qu'ils sont écrits.

Combien de workers ?

Chaque worker Gunicorn est un processus Python complet, avec sa propre mémoire. Avec 2 Go de RAM et 2 vCPU, 2 à 4 workers suffisent pour la plupart des applications. Surveillez la mémoire si votre application charge de gros modèles ou de gros fichiers au démarrage : multipliez par le nombre de workers.

Option 2 : sur un VPS

Le site publié sur nom.vps.heberghub.fr arrive en HTTP sur le port 80 du VPS. L'application écoute en local (port 8000), nginx la relaie sur le port 80.

apt install -y python3-venv git nginx
adduser --system --group --home /opt/mon-app app
git clone https://exemple.org/vous/mon-app.git /opt/mon-app
python3 -m venv /opt/mon-app/.venv
/opt/mon-app/.venv/bin/pip install -r /opt/mon-app/requirements.txt
chown -R app:app /opt/mon-app
install -m 600 -o root -g root /dev/null /etc/mon-app.env
nano /etc/mon-app.env      # SECRET_KEY=..., autres variables

Debian 13 fournit Python 3.13 (vérifié : 3.13.5, avec venv). Certaines anciennes versions de bibliothèques ne s'installent pas sur Python 3.13 : prenez des versions récentes.

# /etc/systemd/system/mon-app.service
[Unit]
Description=Application Python
After=network-online.target
Wants=network-online.target

[Service]
User=app
WorkingDirectory=/opt/mon-app
EnvironmentFile=/etc/mon-app.env
ExecStart=/opt/mon-app/.venv/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 app:app
# FastAPI : ExecStart=/opt/mon-app/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --proxy-headers
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now mon-app
curl -s http://127.0.0.1:8000/sante

Pour nginx, reprenez la configuration du guide Déployer une application Node.js en remplaçant le port 3000 par 8000.

Mise à jour : git pull, .venv/bin/pip install -r requirements.txt, puis systemctl restart mon-app.

Problèmes fréquents

Symptôme Cause probable
502 ou « L'application ne répond pas » Écoute sur 127.0.0.1 dans un conteneur applicatif, ou port différent de celui déclaré.
ModuleNotFoundError Paquet absent de requirements.txt, ou commande lancée hors de l'environnement virtuel.
KeyError: 'SECRET_KEY' Variable non déclarée sur le serveur.
Liens en http:// ou boucle de redirection En-têtes du relais ignorés : ProxyFix (Flask) ou --proxy-headers (Uvicorn).
WORKER TIMEOUT dans les journaux Requête trop longue : déplacez le traitement lourd en tâche de fond, ou augmentez --timeout.
Rien dans les journaux Sortie mise en tampon : PYTHONUNBUFFERED=1.

Voir aussi Lire les journaux de son application.

Besoin d'aide ?

Ouvrez un ticket depuis votre espace client avec le nom de l'application, l'heure et l'extrait des journaux. Jamais de secret dans un ticket.

Retour à la documentation