Pourquoi héberger n8n sur votre VPS
La version cloud de n8n facture à l'exécution au-delà du plan et limite le nombre de workflows actifs. Sur votre VPS, le seul coût est celui du serveur, indépendamment du nombre d'automatisations ou d'appels API. Vous gardez aussi le contrôle total sur les credentials, les charges utiles de webhooks et l'historique des exécutions — aucune donnée ne transite par une infrastructure tierce.
Les bénéfices concrets d'un n8n auto-hébergé
- Exécutions sans limite : aucun plafond mensuel, aucun surcoût à l'échelle.
- Credentials et payloads de webhooks confinés sur votre serveur, sans transit externe.
- Accès aux nodes communautaires et à l'exécution de code personnalisé sans restriction.
- Webhooks sur votre propre domaine, configurables pour chaque intégration entrante.
- Coût prévisible : un prix VPS fixe, indépendant du volume d'automatisations.
- Persistance maîtrisée des workflows et de l'historique dans des volumes que vous sauvegardez.
- Compatibilité avec Ollama, Flowise ou tout autre service Docker sur le même réseau interne.
Prérequis chiffrés
Avant de commencer, vérifiez que votre VPS répond aux exigences suivantes. Pour un usage modéré, 1 vCPU et 1 Go de RAM suffisent ; prévoyez 2 vCPU et 2 Go dès que vous exécutez des workflows lourds ou concurrents, et 4 Go si vous branchez une base PostgreSQL dédiée ou intégrez un modèle IA local. Comptez 10 Go de disque minimum pour Docker, les volumes et les logs. Côté réseau, le port 5678 ne doit pas être exposé directement — n8n n'écoute que sur 127.0.0.1:5678 et toutes les requêtes passent par le reverse proxy sur le port 443. Un sous-domaine (par exemple n8n.votre-domaine.com) pointant vers l'IP du VPS est requis pour HTTPS et les webhooks entrants. Docker Engine 24+ et Docker Compose v2 (plugin, pas le binaire docker-compose standalone) sont nécessaires.
Installation pas à pas
Mettre à jour le VPS et installer Docker
Connectez-vous en SSH et mettez à jour les paquets :
apt update && apt upgrade -y. Installez ensuite Docker via le script officiel :curl -fsSL https://get.docker.com | sh. Vérifiez l'installation :docker --version && docker compose version. Créez le répertoire de travail :mkdir -p /opt/n8n && cd /opt/n8n.Créer le fichier docker-compose.yaml
Créez
docker-compose.yamlavec le service n8n, un volume nommé pour la persistance et les variables d'environnement essentielles. Déclarezimage: n8nio/n8n:latest,restart: unless-stopped, et montezn8n_data:/home/node/.n8n. Exposez uniquement sur127.0.0.1:5678:5678pour éviter toute exposition publique directe du port.Fixer les variables d'environnement critiques
Dans la section
environmentdu service, déclarez au minimum :N8N_HOST=n8n.votre-domaine.com,N8N_WEBHOOK_URL=https://n8n.votre-domaine.com/,N8N_PROXY_HOPS=1(pour que n8n accepte les en-têtesX-Forwarded-*du reverse proxy),N8N_BASIC_AUTH_ACTIVE=true,N8N_BASIC_AUTH_USER=<votre-login>etN8N_BASIC_AUTH_PASSWORD=<mot-de-passe-fort>. Pour la production, stockez ces valeurs dans un fichier.envadjacent et référencez-le viaenv_file: .env.Démarrer le conteneur
Lancez :
docker compose up -d. Vérifiez que le conteneur tourne :docker compose ps. Consultez les logs :docker compose logs -f n8n. n8n est prêt quand la ligneEditor is now accessible via: http://localhost:5678/apparaît dans les logs. L'interface n'est accessible qu'en127.0.0.1à ce stade — c'est intentionnel.Configurer le reverse proxy avec Caddy (recommandé)
Caddy est le reverse proxy le plus simple pour n8n : il gère le certificat Let's Encrypt et le renouvellement sans configuration supplémentaire. Installez Caddy (
apt install caddy) puis éditez/etc/caddy/Caddyfilepour ajouter :n8n.votre-domaine.com { reverse_proxy localhost:5678 }. Rechargez :systemctl reload caddy. Avec nginx, ajoutez dans votre bloclocation:proxy_set_header X-Forwarded-Host $host;,proxy_set_header X-Forwarded-Proto $scheme;etproxy_set_header X-Real-IP $remote_addr;— sans ces en-têtes, n8n reconstruit des URL de webhooks incorrectes.Vérifier les webhooks entrants
Dans l'éditeur n8n, créez un workflow de test avec un node Webhook. L'URL affichée en mode production doit être exactement
https://n8n.votre-domaine.com/webhook/<votre-chemin>. Déclenchez l'appel depuis votre machine locale :curl -X POST https://n8n.votre-domaine.com/webhook/test -d '{}'. Si l'URL affichée contientlocalhostou le port5678, la variableN8N_WEBHOOK_URLn'est pas prise en compte — vérifiez que le conteneur a redémarré après l'ajout de la variable.Brancher PostgreSQL pour la production
SQLite convient pour tester ; en production, préférez PostgreSQL. Ajoutez un service
postgres:15au mêmedocker-compose.yaml, avec un volume dédiépg_data. Dans le service n8n, ajoutez :DB_TYPE=postgresdb,DB_POSTGRESDB_HOST=postgres,DB_POSTGRESDB_DATABASE=n8n,DB_POSTGRESDB_USER=n8n,DB_POSTGRESDB_PASSWORD=<mot-de-passe>. Redémarrez l'ensemble :docker compose up -d. La base SQLite n'est pas migrée automatiquement — exportez vos workflows avant de basculer.
Durcissement et mode queue
Trois réflexes de production : (1) Ne jamais exposer le port 5678 sur l'interface publique — gardez la liaison 127.0.0.1:5678. (2) Activez l'authentification : en v1.x, N8N_BASIC_AUTH_ACTIVE=true ; en v1.27+, préférez l'authentification native via l'interface (Paramètres → Sécurité). (3) Pour des workflows IA ou à longue durée, passez en mode queue avec une instance worker séparée et une file Redis : cela isole les exécutions longues de l'interface et permet de scaler horizontalement sans reconfigurer les webhooks.
Sécuriser les exécutions persistées (advisory GHSA-vrv8-j27g-g7cr)
En août 2026, l'advisory GHSA-vrv8-j27g-g7cr a révélé que trois nodes versent les credentials déchiffrés dans les données d'exécution persistées en cas d'erreur : le node Strapi (tokens OAuth écrits en clair dans execution_data), le node SeaTable (clés API inscrites dans les logs d'exécution d'erreur) et le node Mailcheck (identifiants SMTP persistés dans execution_data). Ces secrets restent lisibles par tout utilisateur ayant accès aux logs d'exécution dans l'interface n8n. Sur une instance sans pruning, ils s'accumulent indéfiniment.
Quatre gestes pour corriger et prévenir
Mettre à jour vers la version corrigeant l'advisory
La mise à jour est le seul correctif complet pour les trois nodes. Vérifiez votre version courante :
docker exec n8n n8n --version. Pulls la dernière image et relancez :docker compose pull && docker compose up -d. Pour la branche stable, la correction est disponible à partir de la version 2.35.4 ; pour la branche v1, à partir de la version 1.123.73. Confirmez l'absence de credentials exposés dans les logs d'erreur après mise à jour.Activer le pruning des exécutions
Ajoutez deux variables dans la section
environmentdu service n8n :EXECUTIONS_DATA_PRUNE=trueetEXECUTIONS_DATA_MAX_AGE=168(7 jours, exprimé en heures). Redémarrez le conteneur :docker compose restart n8n. Le pruning supprime les données d'exécution au-delà de l'âge défini. Il réduit la surface d'exposition mais ne remplace pas la mise à jour — des données exposées avant le pruning ont déjà pu être lues.Restreindre l'accès aux logs d'exécution
En mode multi-utilisateurs, vérifiez dans Paramètres → Utilisateurs que seuls les administrateurs peuvent consulter les logs d'exécution. Ce garde-fou limite la propagation si des données sensibles ont été persistées avant la mise à jour. Sur une instance mono-utilisateur exposée sur un serveur partagé, vérifiez que le port 5678 n'est pas accessible directement depuis l'extérieur.
Mesure conservatoire avant mise à jour
Sur une instance non encore mise à jour : désactivez ou retirez les workflows utilisant les nodes Strapi, SeaTable ou Mailcheck. Le problème ne se déclenche qu'en cas d'erreur d'exécution sur ces nodes, mais l'erreur peut survenir sur une coupure réseau ou un token expiré — des conditions hors de votre contrôle direct.
Fatal V8 crash avec plus de 30 workflows actifs
Symptôme. L'instance n8n s'arrête brutalement avec l'erreur FATAL ERROR: invalid-mark-compact are transition dans les logs Docker. Le conteneur redémarre si restart: unless-stopped est configuré, mais le crash se reproduit dès que la charge repasse le même seuil. Aucun message d'erreur dans l'interface : le conteneur disparaît sans prévenir.
Cause. Cette erreur est une panique du moteur V8 (le moteur JavaScript embarqué dans Node.js) lors d'un cycle de garbage collection. Elle se déclenche quand le tas mémoire de V8 est saturé — typiquement à partir de 30 workflows s'exécutant simultanément sur un VPS avec moins de 2 Go de RAM alloués au processus. Chaque workflow actif maintient un contexte d'exécution en mémoire ; au-delà d'un certain seuil, le GC tente une transition mark-compact sur un tas déjà corrompu, ce qui provoque le crash fatal. Le problème n'a pas de recovery automatique : n8n ne dispose pas d'un mécanisme de dégradation gracieuse à ce niveau.
Fix immédiat. Augmentez la limite de tas V8 en ajoutant la variable d'environnement suivante dans le service n8n de votre docker-compose.yaml : NODE_OPTIONS=--max-old-space-size=2048. Cela alloue 2 Go au tas V8. Redémarrez le conteneur : docker compose restart n8n. Observez les logs pendant quelques minutes pour confirmer l'absence de crash.
Fix structurel. La limite mémoire du conteneur doit correspondre à la limite V8. Si votre docker-compose.yaml déclare mem_limit: 1g et que vous passez --max-old-space-size=2048, le OOM killer tue le conteneur avant que V8 ne puisse l'utiliser. Règle : allouez au moins 2 Go de RAM à la VM au-delà de 30 workflows actifs, et passez NODE_OPTIONS=--max-old-space-size=1536 (laissez 512 Mo pour le reste du système). Pour une installation avec plus de 50 workflows, préférez le mode queue (instance worker Redis séparée) qui découple les exécutions du processus principal et répartit la charge mémoire. Source : community.n8n.io thread #308425.
502 Bad Gateway persistant derrière nginx
Symptôme. Les requêtes courtes réussissent mais certains workflows retournent 502 Bad Gateway depuis nginx — notamment les workflows qui appellent des APIs externes lentes, traitent de gros volumes de données ou enchaînent de nombreux nodes. Le 502 se produit exactement 60 secondes après le début de l'exécution, même si n8n continue de travailler en arrière-plan.
Cause. nginx attend par défaut 60 secondes une réponse du backend avant de fermer la connexion (proxy_read_timeout = 60 s). Pour n8n, le backend est le processus qui exécute le workflow : si l'exécution prend plus de 60 secondes, nginx coupe la connexion et retourne un 502. n8n continue l'exécution en arrière-plan (le workflow se termine côté serveur), mais le client ne reçoit jamais la réponse — ce qui fait croire à un échec alors que le résultat a bien été produit. Le comportement est aggravé avec les webhooks synchrones qui attendent la fin du workflow pour répondre (Respond to Webhook node en fin de flux).
Fix. Ajoutez ces deux directives dans le bloc location de votre configuration nginx qui proxie vers n8n :
proxy_read_timeout 300;proxy_send_timeout 300;
La valeur de 300 secondes (5 minutes) couvre la grande majorité des workflows longs. Pour des workflows exceptionnellement longs (import de données massif, chaînes IA multi-étapes), montez à 600 s. Rechargez nginx : nginx -t && systemctl reload nginx. Notez que cette valeur ne remplace pas le timeout de connexion (proxy_connect_timeout, gardez-le à 60 s — il ne s'applique qu'à l'établissement initial de la connexion TCP).
Vérification. Lancez un workflow volontairement lent (node Wait réglé sur 90 s, par exemple) et vérifiez que la réponse arrive au-delà de 60 secondes sans erreur. Si le 502 persiste après la modification, vérifiez que vous avez édité le bon bloc location — une configuration nginx fragmentée en plusieurs fichiers include peut avoir un bloc plus spécifique qui override le timeout. Source : community.n8n.io thread #274581.
Breaking changes à connaître (v1.27–v1.31)
Trois changements des versions récentes peuvent casser silencieusement une instance existante.
Renommage du paramètre OAuth 2.0 dans HTTP Request. Le champ oauthTokenData a été renommé dans les versions 1.27-1.31. Les requêtes authentifiées via OAuth 2.0 dans le node HTTP Request peuvent cesser de fonctionner sans message d'erreur explicite — n8n envoie une requête non authentifiée plutôt que de lever une exception. Vérifiez chaque workflow qui utilise ce node avec des credentials OAuth après une mise à jour.
Format de l'URL de webhook. Le format de l'URL générée par les nodes Webhook a changé dans cette plage de versions. Si vous avez copié des URL de webhook en dur dans des services tiers (Stripe, GitHub, Slack…), re-vérifiez-les après la mise à jour.
Dépréciation de $item(). La fonction $item() disponible dans les expressions et le node Function est marquée dépréciée. Son remplacement est $input.item pour l'item courant ou $('Nom du node').item pour les items d'un node précédent. Elle fonctionne encore en v1.x.
La liste complète des breaking changes est disponible sur la documentation officielle n8n.
Dépannage des erreurs courantes
Le port 5678 est inaccessible. Vérifiez que le conteneur tourne (docker compose ps) et que vous écoutez bien sur 127.0.0.1:5678. Si vous testez depuis la machine locale, curl http://127.0.0.1:5678/ doit répondre.
Les webhooks affichent localhost au lieu du domaine. La variable N8N_WEBHOOK_URL est absente ou mal définie. Ajoutez N8N_WEBHOOK_URL=https://n8n.votre-domaine.com/ (avec le slash final) et redémarrez : docker compose restart n8n.
N8N_PROXY_HOPS manquant : erreur 502 ou boucle. Sans cette variable, n8n rejette ou boucle sur les en-têtes X-Forwarded-*. Ajoutez N8N_PROXY_HOPS=1 dans l'environnement du conteneur.
Erreur de permissions sur le volume. Le conteneur n8n tourne sous l'UID 1000. Si le dossier de données a été créé par root, les permissions sont incorrectes : chown -R 1000:1000 /opt/n8n/data puis docker compose restart n8n.
La base PostgreSQL refuse la connexion. Vérifiez que le service postgres est sur le même réseau Docker que n8n (docker network inspect <nom>) et que les variables DB_POSTGRESDB_HOST, DB_POSTGRESDB_USER et DB_POSTGRESDB_PASSWORD correspondent exactement à celles définies dans le service postgres.
Crash V8 brutal (FATAL ERROR: invalid-mark-compact). Le tas mémoire de V8 est saturé : plus de 30 workflows simultanés avec une RAM insuffisante. Ajoutez NODE_OPTIONS=--max-old-space-size=2048 dans l'environnement du service n8n et augmentez la RAM du VPS à 2 Go minimum. Voir la section dédiée ci-dessus.
502 Bad Gateway persistant (exactement 60 s). Le proxy_read_timeout nginx est au défaut (60 s). Portez-le à 300 s dans le bloc location nginx pointant vers n8n. Voir la section dédiée ci-dessus.
Credentials visibles dans les logs d'exécution. Vous utilisez un des nodes Strapi, SeaTable ou Mailcheck sur une version antérieure au correctif de l'advisory GHSA-vrv8-j27g-g7cr. Mettez à jour vers la version corrigeant l'advisory et activez le pruning des exécutions. Voir la section dédiée ci-dessus.
SQLite rollback silencieux sous charge. Symptôme : exécutions manquantes ou résultats incohérents sans message d'erreur visible, uniquement sous forte concurrence. Cause : SQLite ne supporte pas les écritures concurrentes ; au-delà de quelques workflows simultanés, des transactions silencieuses échouent en rollback (issue GitHub n8n #32284). Solution unique : migrer vers PostgreSQL.
SQLite ou PostgreSQL : le choix qui change tout en production
SQLite est un moteur de test, pas un moteur de production. Il convient parfaitement pour découvrir n8n sur un poste local ou valider un workflow avant déploiement. Dès que votre instance reçoit du trafic réel — plusieurs workflows déclenchés en parallèle, webhooks entrants, exécutions planifiées simultanées — SQLite devient un risque. L'issue GitHub n8n #32284 documente des rollbacks silencieux sous charge concurrente : des exécutions disparaissent de l'historique sans message d'erreur, et des résultats intermédiaires s'écrivent de façon partielle. Le problème est architectural : SQLite ne supporte pas les écritures concurrentes, et n8n en génère dès qu'il traite plusieurs workflows en parallèle.
Migrer vers PostgreSQL. Le fichier docker-compose.yaml officiel inclut déjà un service postgres. La procédure de migration en trois étapes préserve tous vos workflows :
1. Exportez les workflows existants depuis l'instance SQLite en cours : n8n export:workflow --all --output=/opt/n8n/workflows-backup.json
2. Modifiez le fichier .env pour basculer le moteur : DB_TYPE=postgresdb avec les variables DB_POSTGRESDB_HOST, DB_POSTGRESDB_DATABASE, DB_POSTGRESDB_USER et DB_POSTGRESDB_PASSWORD pointant vers le service postgres de votre Compose.
3. Redémarrez l'ensemble (docker compose up -d) puis importez les workflows : n8n import:workflow --input=/opt/n8n/workflows-backup.json.
Stockage persistant indépendant. Le volume PostgreSQL (pg_data) doit être monté sur un stockage persistant indépendant de l'hôte — un volume Docker nommé (volumes: en bas du Compose) ou un montage bind sur un répertoire sauvegardé. Si le conteneur postgres est détruit et recréé sans volume nommé ou bind, toute la base est perdue. Vérifiez systématiquement avec docker volume ls que le volume existe bien après un docker compose up -d.
Dimensionnement. PostgreSQL consomme environ 50 à 100 Mo de RAM supplémentaire par rapport à SQLite sur une charge légère. Prévoyez au minimum 2 Go de RAM pour l'ensemble n8n + PostgreSQL sur le même VPS ; 4 Go si vous exécutez régulièrement des workflows IA ou des imports volumineux.
Quatre configurations critiques pour une production stable
Ces quatre points concentrent l'essentiel des incidents signalés sur community.n8n.io et DEV Community en 2026. Chacun est indépendant et peut être appliqué sur une instance existante sans redéploiement complet.
1. N8N_WEBHOOK_URL est obligatoire dans le .env. Sans cette variable, n8n reconstruit l'URL de webhook en combinant le hostname interne du conteneur et le port 5678. Le résultat ressemble à http://n8n_container:5678/webhook/… — une URL inutilisable depuis un service externe. Pire : l'URL change à chaque recréation du conteneur si vous utilisez docker compose down && docker compose up. Posez N8N_WEBHOOK_URL=https://n8n.votre-domaine.com/ (avec le slash final) dans votre .env et ne l'omettez jamais, même sur un déploiement de test accessible depuis l'extérieur.
2. RAM minimale 2 Go, recommandée 4 à 8 Go. Un VPS à 1 Go de RAM est insuffisant dès que n8n exécute plusieurs workflows simultanément. L'OOM killer Linux tue le processus Node.js sans log explicite dans n8n — le conteneur disparaît et restart: unless-stopped le relance, créant un cycle de crashs difficile à diagnostiquer. Le minimum documenté par n8n pour une utilisation en production est 2 Go ; 4 à 8 Go sont recommandés pour des instances avec des workflows IA, des agents ou plus de 20 workflows actifs. Si vous ne pouvez pas augmenter la RAM immédiatement, réduisez le nombre de workers et activez le mode queue pour isoler les exécutions longues.
3. En-têtes WebSocket nginx obligatoires pour l'éditeur. L'interface n8n communique avec le backend via WebSocket pour l'exécution en temps réel et les mises à jour d'état. Sans les deux en-têtes suivants dans le bloc location nginx, la connexion WebSocket est refusée et l'éditeur devient inaccessible ou se fige :
proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";
Ces en-têtes sont distincts des en-têtes de forwarding d'IP déjà mentionnés. Caddy les gère automatiquement via reverse_proxy ; avec nginx, ils doivent être ajoutés explicitement. Symptôme typique sans eux : la page de l'éditeur se charge mais les exécutions ne démarrent pas ou l'interface se déconnecte après quelques secondes.
4. Purge automatique de l'historique d'exécution. Sans purge, la table des exécutions grossit indéfiniment. Sur une instance active pendant plusieurs mois, elle peut atteindre plusieurs gigaoctets et dégrader significativement les performances — temps de chargement des listes d'exécutions, lenteur de l'interface, voire saturation du disque. Activez la purge automatique avec deux variables d'environnement :
EXECUTIONS_DATA_PRUNE=trueEXECUTIONS_DATA_MAX_AGE=168
La valeur 168 correspond à 7 jours exprimés en heures. Ajustez selon votre besoin de rétention — 720 pour 30 jours, 336 pour 14 jours. Ces variables s'ajoutent dans la section environment du service n8n et prennent effet au prochain démarrage du conteneur.
Maintenir et faire évoluer l'instance
Pour les mises à jour, deux approches : Watchtower (surveillance automatique de l'image et redémarrage) ou une tâche cron manuelle (docker pull n8nio/n8n:latest && docker compose up -d). Dans les deux cas, lisez les notes de version avant une mise à jour majeure — les breaking changes listés ci-dessus sont représentatifs du rythme des changements. Gardez vos exports de workflows à jour dans un dépôt git : c'est la sauvegarde la plus rapide à restaurer en cas d'incident.
Pour les advisories de sécurité, suivez la page GitHub Security Advisories du dépôt n8n — les versions stables publient les correctifs critiques rapidement, et chaque advisory liste les versions affectées et la version minimale corrigeant le problème.