Pourquoi héberger Apache Airflow sur votre propre VPS
Les orchestrateurs cloud comme Cloud Composer (Google) ou MWAA (Amazon) embarquent Airflow en service managé, mais facturent l'environnement à l'heure — indépendamment de votre charge réelle. En self-hébergeant sur un VPS, vous payez uniquement le serveur et vos DAG s'exécutent au plus près de vos sources de données internes, sans transit réseau externe. La liberté va plus loin : vous choisissez la version d'Airflow, les providers Python, les connexions à vos bases, l'exécuteur (Local, Celery, Kubernetes). Pour des pipelines qui tournent sur des données sensibles ou des environnements réglementés, rester on-premise avec un VPS dédié n'est pas une option mais une contrainte. Enfin, le coût : un VPS 4 vCPU / 8 Go RAM revient à une fraction d'un environnement managé pour la même charge de travail quotidienne.
Ce qu'Airflow orchestre concrètement
- Data pipelines et ETL — extraire depuis des API ou des bases, transformer et charger vers un data warehouse.
- Workflows ML — entraînement de modèles, évaluation, déploiement automatisé avec des dépendances entre étapes.
- Tâches planifiées complexes — backfills, catchups, gestion des échecs avec retry configurable par tâche.
- Intégrations tierces — plus de 80 providers officiels : PostgreSQL, MySQL, S3, BigQuery, dbt, Spark, Kubernetes, HTTP.
- Pipelines de rapport — génération automatique de rapports périodiques et envoi par e-mail ou Slack.
- Orchestration de microservices — déclencher des jobs distants via API et attendre leur résultat avant d'enchaîner.
Prérequis VPS et stack technique
Airflow est la stack la plus gourmande en ressources de cette catégorie car elle fait tourner en parallèle plusieurs services. Pour la production : 4 vCPU et 8 Go de RAM minimum — choisissez un VPS Power ou un VPS dédié pour commencer. Pour le développement ou les tests : 2 vCPU et 4 Go de RAM suffisent en LocalExecutor (sans Celery ni Redis). Prévoyez 40 Go de disque SSD pour les logs et les métadonnées. Sur le plan logiciel : Docker 24+ et Docker Compose v2 (vérifiez avec docker compose version, pas l'ancien docker-compose v1 qui ne supporte pas tous les healthchecks), Ubuntu 22.04 LTS ou Debian 12, et un nom de domaine pointant vers votre VPS pour le reverse proxy HTTPS.
Architecture Docker Compose d'Airflow
Le fichier docker-compose.yaml officiel d'Airflow déploie six services qui forment une architecture complète. Le webserver sert l'interface graphique sur le port 8080 : visualisation des DAG, déclenchement manuel, consultation des logs. Le scheduler est le cœur du système : il analyse les DAG, planifie les tâches selon leur schedule et leurs dépendances, et les remet aux workers. Le worker exécute les tâches confiées par le scheduler — vous pouvez en lancer plusieurs pour la parallélisation. Le triggerer gère les tâches deferables (sensors asynchrones Airflow 2.2+) sans bloquer de slots worker. PostgreSQL est la base de métadonnées d'Airflow : état des DAG runs, logs structurés, XComs, variables et connexions — c'est le composant le plus critique. Redis sert de broker de messages entre le scheduler et les workers Celery : chaque tâche prête à l'exécution transite par une queue Redis avant d'être prise en charge par un worker disponible.
Déployer Airflow 3.x avec Docker Compose
Récupérer le docker-compose officiel et créer les répertoires
Créez le répertoire de travail, descendez le fichier de référence et préparez les dossiers montés en volumes :
mkdir -p /opt/airflow && cd /opt/airflow curl -LfO 'https://airflow.apache.org/docs/apache-airflow/stable/docker-compose.yaml' mkdir -p ./dags ./logs ./plugins ./configCes dossiers doivent exister avant de lancer les conteneurs. Si Docker les crée lui-même, il leur attribue le propriétaire
rootet Airflow ne peut pas y écrire.Configurer le fichier .env (UID, PostgreSQL, clé secrète)
Créez
/opt/airflow/.envavec les variables essentielles :echo "AIRFLOW_UID=$(id -u)" > .env echo "AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres/airflow" >> .env echo "AIRFLOW__CORE__FERNET_KEY=$(python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())')" >> .envAIRFLOW_UIDaligne les permissions des volumes sur votre utilisateur système.AIRFLOW__DATABASE__SQL_ALCHEMY_CONNforce PostgreSQL — la valeur par défaut du compose officiel utilise déjà PostgreSQL, mais la rendre explicite dans.envévite toute confusion.FERNET_KEYchiffre les connexions et variables stockées en base — générez-la une seule fois et sauvegardez-la : si vous la perdez, toutes vos connexions secrètes deviennent illisibles.Initialiser la base de données
Appliquez les migrations et créez le premier compte administrateur :
docker compose up airflow-initLe conteneur
airflow-initse connecte à PostgreSQL, applique toutes les migrations de schéma et crée l'utilisateurairflow(mot de passeairflow). Attendez l'exit code0— un code différent indique un problème de connexion à la base (PostgreSQL pas encore healthy) ou une image manquante. Changez le mot de passe dès votre première connexion via l'UI ou la commandedocker compose exec airflow-webserver airflow users reset-password.Démarrer tous les services
Lancez la stack complète en arrière-plan :
docker compose up -d docker compose psTous les conteneurs doivent passer à l'état
healthyen une à trois minutes. Si un service reste enstarting, consultez ses logs :docker compose logs airflow-scheduler --tail=50Le webserver est accessible sur
http://localhost:8080. N'ouvrez pas ce port publiquement — passez par un reverse proxy.Créer un compte administrateur personnalisé
Le compte
airflow/airflowcréé parairflow-initest à usage unique. Créez votre compte permanent :docker compose exec airflow-webserver airflow users create \ --username admin \ --firstname Prenom \ --lastname Nom \ --role Admin \ --email [email protected] \ --password VotreMotDePasseSolideSupprimez ensuite le compte par défaut via
Admin → Usersdans l'interface web.Exposer Airflow derrière un reverse proxy avec SSL
N'exposez jamais le port 8080 directement — l'UI Airflow affiche des connexions, des variables et des logs sensibles. Avec Caddy (recommandé pour la simplicité du TLS automatique) :
apt install -y caddy cat > /etc/caddy/Caddyfile << 'EOF' airflow.votredomaine.com { reverse_proxy localhost:8080 } EOF systemctl reload caddy ufw deny 8080Caddy obtient et renouvelle automatiquement le certificat Let's Encrypt. Avec nginx, créez un vhost standard en
proxy_pass http://127.0.0.1:8080et bloquez le port avecufw deny 8080.
Configurer PostgreSQL comme backend de métadonnées
Ne jamais utiliser SQLite en production avec Airflow sur Docker. SQLite n'est pas conçu pour des accès concurrents multiples : le scheduler, le webserver et les workers accèdent à la base simultanément, et SQLite sur un volume Docker a des limites de verrouillage qui causent des corruptions et des pertes de données — le projet Airflow documente explicitement cet avertissement depuis la version 2.0. Le fichier docker-compose.yaml officiel inclut déjà un service PostgreSQL 13, mais vérifiez que votre .env force bien la connexion :
AIRFLOW__DATABASE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres/airflowPour une production robuste, personnalisez le mot de passe PostgreSQL. Dans docker-compose.yaml, cherchez la section postgres et modifiez POSTGRES_PASSWORD. Mettez à jour AIRFLOW__DATABASE__SQL_ALCHEMY_CONN en conséquence. Sauvegardez la base PostgreSQL régulièrement avec docker compose exec postgres pg_dump -U airflow airflow > backup_$(date +%Y%m%d).sql. Si vous gérez déjà un serveur PostgreSQL externe, supprimez le service postgres du compose et pointez SQL_ALCHEMY_CONN vers votre instance : c'est la configuration recommandée pour les installations multi-VPS.
Passer au CeleryExecutor pour plusieurs workers
Le LocalExecutor (défaut dans certaines configs) exécute les tâches dans des sous-processus du scheduler — simple, mais limité à un seul nœud. Le CeleryExecutor découple l'exécution : le scheduler pousse les tâches dans une queue Redis, et un ou plusieurs workers les consomment indépendamment. Le fichier docker-compose.yaml officiel active CeleryExecutor par défaut. Pour scale-out, dupliquez simplement le service worker :
docker compose up -d --scale airflow-worker=3Cela démarre trois workers qui consomment la même queue Redis en parallèle. Chaque worker prend en charge parallelism tâches simultanées (configurable via AIRFLOW__CORE__PARALLELISM, défaut 32). Pour distribuer les workers sur plusieurs VPS, pointez-les tous vers le même Redis et le même PostgreSQL — c'est l'architecture Celery standard. Ajoutez AIRFLOW__CELERY__WORKER_CONCURRENCY=8 dans .env pour affiner la concurrence par worker selon les ressources disponibles sur chaque nœud.
Gérer les connexions et les variables secrètes
Airflow chiffre les connexions et variables sensibles en base grâce à la FERNET_KEY configurée dans .env. Via l'UI : Admin → Connections → Add Connection — renseignez l'URI complète ou les champs individuels (host, login, password, port). Ces valeurs sont chiffrées avec Fernet avant écriture en base. Via les variables d'environnement (recommandé pour les CI/CD) : une variable d'env préfixée AIRFLOW_CONN_ surpasse toute connexion enregistrée en base. Exemple dans .env :
AIRFLOW_CONN_MON_POSTGRES=postgresql://user:password@host:5432/dbnameLe nom de la connexion dans vos DAG est alors mon_postgres (le préfixe et les majuscules sont retirés). Secret Backend : pour les environnements Kubernetes ou cloud, configurez AIRFLOW__SECRETS__BACKEND=airflow.providers.amazon.aws.secrets.secrets_manager.SecretsManagerBackend (ou HashiCorp Vault, GCP Secret Manager) — Airflow résoudra les connexions depuis votre coffre-fort plutôt que depuis PostgreSQL. Variables : Admin → Variables dans l'UI, ou via env AIRFLOW_VAR_NOM_DE_LA_VARIABLE. Évitez de stocker des secrets dans les Variables non chiffrées — préférez les Connections ou le Secret Backend.
Créer et tester votre premier DAG Python
Créer le fichier DAG dans le dossier /dags
Créez
/opt/airflow/dags/mon_premier_dag.py:from airflow.sdk import DAG, task from datetime import datetime with DAG( dag_id='mon_premier_dag', schedule='@daily', start_date=datetime(2025, 1, 1), catchup=False, tags=['exemple'], ) as dag: @task def extraire(): return {'lignes': 42} @task def transformer(donnees: dict): print(f"Traitement de {donnees['lignes']} lignes") return donnees['lignes'] * 2 @task def charger(resultat: int): print(f"Résultat final : {resultat}") charger(transformer(extraire()))Notez l'utilisation de
schedule(et nonschedule_interval, supprimé en Airflow 3).Vérifier que le scheduler parse le DAG sans erreur
Le scheduler détecte et parse automatiquement les fichiers
.pydu dossierdags. Vérifiez manuellement :docker compose exec airflow-scheduler airflow dags list | grep mon_premier_dagSi le DAG n'apparaît pas après 30 secondes, cherchez les erreurs de parsing :
docker compose logs airflow-scheduler | grep -i 'error\|exception' | tail -20Une erreur d'import Python dans le fichier DAG empêche toute la détection — corrigez la syntaxe et le scheduler relancera automatiquement le parsing.
Déclencher une exécution manuelle et inspecter les logs
Dans l'UI Airflow (
https://airflow.votredomaine.com), naviguez vers DAGs, trouvezmon_premier_dag, activez-le (toggle), puis cliquez sur ▶ Trigger DAG. Suivez l'exécution en temps réel dans la vue Graph ou Grid. Cliquez sur une tâche, puis sur Logs pour voir la sortie standard de la fonction. Depuis le terminal :docker compose exec airflow-scheduler airflow dags trigger mon_premier_dag docker compose exec airflow-scheduler airflow dags state mon_premier_dag $(date +%Y-%m-%dT%H:%M:%S+00:00)
Sécuriser votre installation Airflow
Une installation Airflow expose potentiellement vos connexions de bases de données, vos variables et vos logs d'exécution. Appliquez ces mesures dès le déploiement. Authentification : par défaut, Airflow 3 utilise FAB (Flask-AppBuilder) avec authentification par mot de passe. Pour une organisation, connectez Airflow à votre annuaire via AIRFLOW__WEBSERVER__AUTH_BACKEND=airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager avec LDAP, ou activez OAuth2 (GitHub, Google, Azure AD) en configurant le provider FAB correspondant. Désactiver les DAG d'exemple : ajoutez dans .env :
AIRFLOW__CORE__LOAD_EXAMPLES=FalseCes DAG fictifs encombrent l'UI et peuvent induire en erreur. Pare-feu sur le port 8080 : ufw deny 8080 — seul le reverse proxy doit y accéder depuis localhost. Pas de clé Fernet dans les logs : ne loggez jamais os.environ, qui imprimerait la FERNET_KEY en clair. Rotation des secrets : si vous devez changer la FERNET_KEY, utilisez airflow db rotate-fernet-key pour rechiffrer toutes les connexions stockées avant de mettre à jour la variable.
LocalExecutor vs CeleryExecutor vs KubernetesExecutor
Faites défiler le tableau
| Critère | LocalExecutor | CeleryExecutor | KubernetesExecutor |
|---|---|---|---|
| Ressources nécessaires | 2 vCPU / 4 Go RAM | 4 vCPU / 8 Go RAM + Redis | Cluster Kubernetes requis |
| Parallélisme | Limité au nombre de CPU du scheduler | Horizontal — autant de workers que nécessaire | Un pod par tâche — parallélisme quasi sans borne |
| Isolation des tâches | Sous-processus partagés | Workers séparés, même image | Pod isolé par tâche, image configurable |
| Complexité opérationnelle | Simple — pas de Redis | Modérée — Redis à maintenir | Élevée — nécessite Kubernetes |
| Cas d'usage idéal | Dev, tests, pipelines légers | Production VPS, charge variable | Cloud-native, isolation forte requise |
Mettre à jour Airflow sans coupure
Pour une mise à jour rolling sans interrompre les DAG en cours : commencez par mettre à jour l'image dans docker-compose.yaml (ex. apache/airflow:3.3.1). Arrêtez uniquement le webserver et le scheduler, appliquez les migrations, puis redémarrez service par service. Les workers Celery continuent d'exécuter les tâches déjà démarrées pendant la migration :
docker compose stop airflow-webserver airflow-scheduler
docker compose pull
docker compose up -d airflow-webserver airflow-scheduler
docker compose exec airflow-scheduler airflow db upgrade
docker compose up -dToujours consulter le CHANGELOG officiel entre les versions mineures — certaines migrations de schéma sont irréversibles. Gardez une sauvegarde PostgreSQL fraîche avant toute montée de version.
Déboguer et lire les logs Airflow
Airflow génère des logs à plusieurs niveaux et comprendre leur organisation accélère considérablement le débogage. Logs de tâches : chaque tâche écrit dans ./logs/dag_id/run_id/task_id/. Accessibles depuis l'UI (clic sur la tâche → Logs) ou en ligne de commande :
docker compose exec airflow-scheduler airflow tasks logs mon_premier_dag extraire 2025-01-01Logs du scheduler : le plus important pour diagnostiquer les DAG qui ne se déclenchent pas :
docker compose logs airflow-scheduler --follow --tail=100Remote logging : pour des installations multi-workers ou des logs à longue rétention, déportez vers S3 ou GCS :
AIRFLOW__LOGGING__REMOTE_LOGGING=True
AIRFLOW__LOGGING__REMOTE_BASE_LOG_FOLDER=s3://mon-bucket/airflow-logs
AIRFLOW__LOGGING__REMOTE_LOG_CONN_ID=mon_awslog_filename_template : le nom du fichier de log est configurable via AIRFLOW__LOGGING__LOG_FILENAME_TEMPLATE. Le défaut produit des chemins courts mais vous pouvez l'ajuster pour mieux organiser les logs par date. Erreurs silencieuses : si un DAG disparaît de l'UI sans message, cherchez les import errors du scheduler :
docker compose exec airflow-webserver airflow dags list-import-errorsPurge des logs anciens : configurez AIRFLOW__LOG_RETENTION_DAYS=30 pour éviter la saturation du disque — le dossier ./logs peut rapidement atteindre plusieurs gigaoctets sur des pipelines fréquents.