SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

Pourquoi n8n se déconnecte sur votre VPS

Quatre pannes provoquent le même symptôme n8n hors ligne : bannière websocket, boucle de redémarrage, kill OOM ou planning inactif. Identifiez la bonne cause.

Pourquoi n8n se déconnecte : quatre pannes, un seul symptôme

« n8n se déconnecte » recouvre quatre pannes différentes, qui nécessitent chacune une correction spécifique. L’éditeur affiche une bannière indiquant que la connexion est perdue alors que le conteneur fonctionne normalement. Le conteneur redémarre tout seul. Le kernel tue le processus Node.js parce qu’il utilise trop de mémoire. Ou alors le processus ne présente aucun problème, et un workflow actif ne se déclenche simplement jamais. Si vous modifiez le mauvais paramètre, vous passerez le week-end à résoudre un problème qui n’existe pas.

Déterminez donc la panne concernée avant de modifier la configuration. n8n s’exécute comme un processus Node.js unique, généralement dans un conteneur Docker, derrière un reverse proxy qui termine TLS (Transport Layer Security). Chacune de ces couches peut tomber en panne de manière différente, mais le navigateur les signale toutes avec le même message.

Diagnostiquer dans cet ordre

Exécutez ces commandes sur le VPS (serveur privé virtuel) et lisez les valeurs affichées par votre propre machine. Ne les comparez pas avec des nombres publiés dans un fil de forum. Les valeurs utiles ici décrivent votre serveur, pas celui de quelqu’un d’autre.

docker ps -a --filter name=n8n
docker logs --tail 200 --timestamps n8n
docker inspect n8n | grep -iE 'Status|Running|RestartCount|OOMKilled|ExitCode'
docker stats --no-stream

La colonne STATUS de docker ps -a indique depuis combien de temps le conteneur est dans son état actuel. Comparez cette durée avec le moment où le problème a commencé. Si le conteneur fonctionne depuis bien avant l’apparition de la bannière, n8n n’a jamais été indisponible. Le problème concerne la connexion entre votre navigateur et le backend, c’est-à-dire le chemin websocket décrit dans la section suivante.

RestartCount indique combien de fois Docker a redémarré ce conteneur. Notez le nombre, attendez une minute, puis lisez-le de nouveau. Si le nombre augmente pendant que vous l’observez, le conteneur est dans une boucle de redémarrage. Les lignes du journal juste avant chaque redémarrage indiquent la cause.

OOMKilled est un indicateur vrai ou faux. La valeur True signifie que le noyau Linux a tué le processus parce qu’il a dépassé une limite de mémoire, celle du conteneur ou celle de la machine entière. Ce champ permet de distinguer un arrêt pour dépassement de mémoire de tous les autres types d’arrêt. C’est pourquoi vous devez le consulter avant de formuler une hypothèse.

ExitCode indique le code avec lequel votre conteneur s’est arrêté la dernière fois. Vous n’avez pas besoin de mémoriser la signification de chaque code. Lisez le vôtre, puis consultez la fin de docker logs à partir du même horodatage. La fin du journal et l’indicateur de dépassement de mémoire permettent ensemble de comprendre ce qui s’est produit. Pris séparément, chacun peut vous induire en erreur.

docker stats affiche l’utilisation actuelle de la mémoire à côté de la limite appliquée. Laissez cette commande s’exécuter dans un deuxième terminal, déclenchez le workflow qui provoque le problème et observez l’évolution de la valeur pendant l’échec.


La bannière de perte de connexion vient généralement du reverse proxy

L’éditeur n8n maintient une connexion push longue durée ouverte vers le backend afin d’afficher la progression des exécutions sur le canvas. Par défaut, cette connexion est une WebSocket. C’est ce que sélectionne N8N_PUSH_BACKEND, dont la valeur par défaut est websocket. Une WebSocket commence par une requête HTTP ordinaire contenant les en-têtes Connection: Upgrade et Upgrade: websocket. Le serveur répond 101 Switching Protocols, puis les deux parties utilisent le même socket TCP dans les deux directions.

Deux problèmes peuvent interrompre ce fonctionnement. Ils se situent tous les deux dans le proxy, pas dans n8n. Le proxy utilise HTTP/1.0 vers le backend ou supprime les en-têtes d’upgrade. L’upgrade ne se produit donc jamais et l’éditeur se reconnecte indéfiniment. Dans l’autre cas, l’upgrade réussit, puis le proxy ferme le socket après une période d’inactivité. Une WebSocket qui ne transporte aucun message ressemble exactement à une connexion inactive. Dans les deux cas, le conteneur fonctionne correctement. La bannière indique simplement que le navigateur a perdu son canal.

Vérifiez-le dans le navigateur avant de modifier quoi que ce soit. Ouvrez les outils de développement, accédez à l’onglet Network, filtrez les requêtes WS, puis rechargez l’éditeur. La requête push doit atteindre 101 Switching Protocols et rester ouverte. Une requête push qui renvoie un code d’état HTTP ordinaire ou qui réapparaît toutes les quelques secondes indique un problème dans le proxy.

Les paramètres nginx qui maintiennent la connexion de l’éditeur

nginx ne transmet pas une mise à niveau de connexion tant que vous ne le lui demandez pas. proxy_pass utilise HTTP/1.0 pour communiquer avec le backend par défaut, et Connection ainsi que Upgrade sont des en-têtes hop-by-hop que nginx supprime lors de la transmission. Vous devez rétablir les deux. Le bloc map doit être placé dans le contexte http, et non à l’intérieur de server. Si le reste du server block ci-dessous ne vous est pas familier, l’explication directive par directive d’un server block nginx décrit le rôle de chaque directive.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
server {
    listen 443 ssl;
    http2 on;
    server_name n8n.example.com;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        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_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

proxy_read_timeout est la ligne que l’on oublie souvent. Sa valeur par défaut est de 60 secondes et elle s’applique aussi à un WebSocket mis à niveau. Un onglet d’éditeur laissé ouvert sur une instance peu active perd donc sa connexion environ une minute après le dernier message transmis. Augmenter cette valeur corrige la bannière qui s’affiche lorsque vous revenez sur un onglet laissé ouvert.

sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'

nginx -T affiche la configuration complète en cours d’exécution, et non le contenu d’un seul fichier. Cela confirme que votre modification est bien chargée. Si aucun fichier parcouru par une directive include ne contient la configuration, une correction pourtant correcte semble ne produire aucun effet.

Indiquez ensuite à n8n qu’il se trouve derrière un proxy, car il construit les URL à partir de ces valeurs.

environment:
  - N8N_HOST=n8n.example.com
  - N8N_PROTOCOL=https
  - N8N_PORT=5678
  - N8N_PROXY_HOPS=1
  - N8N_WEBHOOK_URL=https://n8n.example.com/

N8N_PROXY_HOPS vaut 0 par défaut. n8n considère alors l’adresse de connexion comme l’adresse du client et ignore X-Forwarded-For. Définissez cette valeur sur le nombre de proxies placés devant le conteneur. En août 2026, N8N_WEBHOOK_URL est le nom actuel et l’ancien paramètre WEBHOOK_URL fonctionne encore, mais affiche un avertissement d’obsolescence au démarrage.

Traefik transmet les WebSockets, puis les interrompt à l’expiration du délai

Traefik transmet une requête de mise à niveau WebSocket sans middleware ni labels supplémentaires. Un utilisateur de Traefik qui voit cette bannière rencontre donc généralement un délai d’expiration, et non l’absence d’un en-tête. Les paramètres se trouvent sur l’entryPoint. En août 2026, dans Traefik v3, idleTimeout vaut 180 secondes par défaut et readTimeout vaut 60 secondes par défaut.

entryPoints:
  websecure:
    address: ":443"
    transport:
      respondingTimeouts:
        readTimeout: 0
        idleTimeout: 3600s

Caddy gère automatiquement la mise à niveau dans reverse_proxy et ne nécessite aucune directive pour cela. Si vous ne pouvez pas modifier le proxy parce qu’il est géré par quelqu’un d’autre, remplacez le canal push par N8N_PUSH_BACKEND=sse. Les SSE (server-sent events) correspondent à une réponse HTTP normale maintenue ouverte. Ils fonctionnent donc avec un proxy qui refuse les mises à niveau, mais un délai d’inactivité trop court les interrompt. Le choix du proxy est une décision distincte. La comparaison entre nginx, Caddy et Traefik présente le coût d’exploitation de chacun.

Quand le conteneur redémarre réellement

Si RestartCount augmente, le conteneur échoue et Docker le relance. Comparez les horodatages des journaux avec chaque redémarrage et examinez ce qui s’est produit juste avant. Quatre causes couvrent presque tous les cas : une erreur de configuration qui empêche le démarrage, une base de données qu’n8n ne peut pas joindre, un crash après le démarrage et un kill par manque de mémoire.

Commencez par le volume, car les permissions sont la cause la moins visible. L’image officielle s’exécute avec l’utilisateur non privilégié node et conserve ses données dans /home/node/.n8n. Un bind mount créé par root n’est pas accessible en écriture pour cet utilisateur. Le processus se termine donc au démarrage à chaque tentative, et la restart policy masque le problème derrière une boucle.

docker compose config
docker run --rm -it --entrypoint sh docker.n8n.io/n8nio/n8n -c 'id'
docker exec n8n ls -ld /home/node/.n8n

Un named volume évite entièrement ce problème, car Docker le crée avec les bonnes permissions. Si vous devez utiliser un bind mount, chown le répertoire de l’hôte avec l’identifiant utilisateur numérique affiché par la première commande. Le mappage des propriétaires entre l’hôte et le conteneur mérite d’être compris une fois pour toutes. L’explication sur PUID et PGID détaille comment ces images déterminent l’utilisateur qui écrit les fichiers.

Le kill pour manque de mémoire qui ressemble à un crash

Deux plafonds mémoire distincts s’appliquent au processus n8n, et ils échouent différemment. La limite du control group du conteneur est appliquée par le kernel : si le processus la dépasse, il est tué immédiatement, sans pouvoir rien écrire, et OOMKilled renvoie true. La limite du heap V8 est appliquée à l’intérieur de Node.js : si le processus la dépasse, Node génère une erreur de heap avec une stack trace, puis se termine de lui-même ; OOMKilled renvoie donc false. Dans le navigateur, ces deux cas sont identiques. Dans docker inspect, ils ne diffèrent que d’un champ.

Définissez la limite du heap Node en dessous de la limite du conteneur. Si la limite du heap est la plus élevée des deux, V8 continue ses allocations au-delà du point où le kernel intervient. Son garbage collector n’atteint alors jamais sa propre limite, et vous obtenez toujours l’échec le plus brutal, sans journal à consulter.

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - NODE_OPTIONS=--max-old-space-size=<MiB, below the container limit>
    deploy:
      resources:
        limits:
          memory: <your container limit>

Choisissez ces deux valeurs en fonction des ressources réellement disponibles sur votre VPS, en laissant de la mémoire pour la base de données, le proxy et le système d’exploitation. docker stats --no-stream affiche l’utilisation actuelle à côté de la limite appliquée. Vous pouvez ainsi vérifier que la limite définie est bien celle que Docker a appliquée. Application des limites mémoire de Compose explique quelle clé est prioritaire lorsque plusieurs limites sont définies.

Les données d’exécution s’accumulent sous vos workflows

Une exécution contient la sortie de chaque nœud pendant le traitement, puis n8n stocke ces données. Deux conséquences en découlent. La mémoire maximale d’une exécution dépend du plus gros lot de données que vous lui transmettez. Un workflow qui traite 10000 lignes à la fois est donc un programme différent du même workflow qui en traite 200 à la fois. De plus, la copie stockée continue de grossir jusqu’à sa suppression.

Le pruning traite le second problème. En août 2026, les valeurs par défaut sont le pruning activé, EXECUTIONS_DATA_MAX_AGE à 336 heures (14 jours) et EXECUTIONS_DATA_PRUNE_MAX_COUNT à 10000. Ces valeurs sont élevées pour un petit VPS utilisant SQLite, où un seul fichier contient toutes les données et où le même processus qui sert l’éditeur doit les lire et les écrire.

environment:
  - EXECUTIONS_DATA_PRUNE=true
  - EXECUTIONS_DATA_MAX_AGE=72
  - EXECUTIONS_DATA_PRUNE_MAX_COUNT=1000
  - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
  - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false

EXECUTIONS_DATA_SAVE_ON_SUCCESS=none est le réglage agressif. Il conserve les exécutions en échec pour le débogage et supprime les exécutions réussies. Prenez cette décision explicitement : si un workflow produit un résultat incorrect sans générer d’erreur, vous n’aurez ensuite plus rien à examiner. Le pruning marque d’abord les lignes comme supprimées, puis les retire lors d’un passage ultérieur. SQLite réutilise les pages libérées au lieu de les rendre au système, donc la taille du fichier sur le disque ne diminue pas dès que vous modifiez le réglage.

Pour réduire le pic plutôt que le volume stocké, transférez moins de données par exécution. Divisez les tâches volumineuses en sous-workflows qui renvoient de petits résultats au workflow parent, utilisez le nœud Loop Over Items pour traiter les données par lots et évitez de charger des jeux de données complets dans le nœud Code.

Les fichiers binaires ne doivent pas transiter par la mémoire

N8N_DEFAULT_BINARY_DATA_MODE est défini sur default par défaut, ce qui conserve les données binaires dans la mémoire du processus en cours d’exécution. Chaque fichier téléchargé par un nœud, ainsi que chaque copie transmise au nœud suivant, y reste jusqu’à la fin de l’exécution. Un workflow qui récupère quelques pièces jointes volumineuses peut faire dépasser au processus une limite que les opérations JSON classiques n’approchent jamais. C’est pourquoi le plantage survient avec un workflow précis plutôt qu’après une durée fixe.

environment:
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem

Avec filesystem, les données binaires sont écrites sous N8N_BINARY_DATA_STORAGE_PATH. Ce répertoire se trouve par défaut dans le dossier utilisateur de n8n, et donc sur le même volume que le reste. Vérifiez que le volume dispose de suffisamment d’espace avant de modifier ce paramètre. N8N_PAYLOAD_SIZE_MAX définit la taille maximale, en MiB (mébioctets), des requêtes entrantes adressées au webhook. Sa valeur par défaut est 16. L’augmenter permet d’accepter des requêtes plus volumineuses, au prix d’une consommation mémoire supplémentaire.

Tous les autres services qui utilisent le serveur se disputent la même RAM. Si les arrêts provoqués par l’OOM ont commencé après l’ajout d’un conteneur de base de données, exécuter la base de données dans Docker ou sur l’hôte est le compromis auquel vous devez maintenant consentir.

Politique de redémarrage et reprise après un redémarrage

Un conteneur sans politique de redémarrage reste arrêté après sa sortie et après le redémarrage de l’hôte. restart: unless-stopped le relance dans les deux cas, tout en respectant un conteneur que vous avez arrêté manuellement. restart: always redémarre également un conteneur que vous avez arrêté volontairement, dès que Docker redémarre.

n8n expose un endpoint de santé, indiqué par N8N_ENDPOINT_HEALTH, dont la valeur par défaut est healthz. Vérifiez-le d’abord depuis l’hôte pour confirmer que le chemin est correct sur votre instance.

curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled docker

Un healthcheck seul ne redémarre rien. Compose marque le conteneur comme non sain, puis s’arrête là. Pour avoir un effet, le healthcheck doit être associé à une politique de redémarrage ou à un watcher externe. Écrire un healthcheck réellement actif et faire redémarrer la stack après un redémarrage couvrent ces deux aspects.

Le workflow qui ne se déclenche jamais alors que n8n fonctionne

Celui-ci n’affiche aucune bannière et ne redémarre pas. Le conteneur fonctionne, l’éditeur est accessible et l’exécution attendue n’apparaît pas dans la liste des exécutions. Quatre causes expliquent la plupart des cas.

  • Le workflow n’est pas actif. Un Schedule Trigger s’exécute uniquement sur le chemin de production. Le tester dans le canvas ne planifie donc rien.
  • Le fuseau horaire n’est pas le vôtre. GENERIC_TIMEZONE utilise America/New_York par défaut. Une planification réglée sur 09:00 se déclenche donc à 09:00 dans ce fuseau tant que vous n’avez pas défini GENERIC_TIMEZONE et TZ selon votre fuseau.
  • Les périodes d’arrêt ne sont pas rattrapées. Les triggers sont enregistrés au démarrage de n8n. Une planification arrivée à échéance pendant le redémarrage du conteneur ne s’exécute donc pas en retard. La prochaine exécution a lieu à la prochaine échéance après le démarrage.
  • Le workflow a été désactivé automatiquement. N8N_WORKFLOW_AUTODEACTIVATION_ENABLED est désactivé par défaut. Lorsqu’il est activé, un workflow qui plante continuellement est dépublié. Il ressemble alors exactement à un workflow qui n’a jamais été activé.

Ouvrez la liste des exécutions et filtrez-la sur ce workflow. Une entrée en échec indique un problème dans le workflow. Si l’échec contient une erreur 429 provenant d’un autre service hébergé sur le même serveur, la limite appartient à ce service et non à n8n. La procédure de diagnostic des erreurs 429 de SearXNG explique comment distinguer son propre rate limiter du blocage de l’adresse IP de votre serveur par les moteurs. L’absence totale d’entrée indique un problème de trigger. Les quatre causes ci-dessus sont alors les premières à vérifier.

Modifications à effectuer en premier

  1. Lisez STATUS, RestartCount et OOMKilled sur votre propre conteneur avant de modifier un fichier.
  2. Si le conteneur ne s’est jamais arrêté, corrigez les en-têtes de mise à niveau du proxy et le délai d’expiration d’inactivité.
  3. Si OOMKilled est vrai, définissez délibérément une limite pour le conteneur, fixez la limite maximale du tas Node en dessous de cette valeur et basculez les données binaires vers filesystem.
  4. Si aucun de ces problèmes ne s’est produit, vérifiez que le workflow est actif et que le fuseau horaire de l’instance est le vôtre.

La plupart de ces paramètres se configurent une seule fois, puis restent inchangés sur une installation fonctionnelle. Si vous êtes encore en train de mettre cette installation en place, le guide n8n avec Docker et HTTPS constitue la base à laquelle ces paramètres s’appliquent.

FAQ

Pourquoi l’éditeur n8n affiche-t-il une bannière de perte de connexion alors que le conteneur fonctionne ?

L’éditeur maintient une connexion WebSocket pour transmettre la progression des exécutions. Si votre reverse proxy ne transmet pas les en-têtes Connection: Upgrade et Upgrade: websocket, ou n’utilise pas HTTP/1.1 vers le backend, la mise à niveau n’aboutit jamais et le navigateur se reconnecte indéfiniment alors que n8n fonctionne normalement. Dans nginx, vous devez utiliser proxy_http_version 1.1 ainsi que les deux lignes proxy_set_header, et définir un proxy_read_timeout supérieur à la valeur par défaut de 60 secondes pour éviter l’interruption d’un onglet inactif. Vérifiez la configuration active avec sudo nginx -T, et non le fichier que vous avez modifié.

Comment distinguer un arrêt pour manque de mémoire d’un crash classique ?

Exécutez docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' et consultez l’indicateur OOMKilled. La valeur True signifie que le kernel a tué le processus parce qu’il a dépassé une limite mémoire. Le journal du conteneur ne contiendra alors rien d’utile, car le processus n’a pas pu écrire. La valeur False, accompagnée d’une erreur de heap et d’une stack trace à la fin de docker logs, signifie que Node.js a atteint sa propre limite de heap V8 et s’est arrêté de lui-même. Définissez NODE_OPTIONS=--max-old-space-size à une valeur inférieure à la limite de votre conteneur afin d’obtenir le second type d’échec, le seul qui laisse des traces exploitables.

La suppression des données d’exécution libère-t-elle immédiatement de l’espace disque ?

Non. EXECUTIONS_DATA_PRUNE marque les anciennes exécutions pour suppression, puis une opération ultérieure les supprime, selon la périodicité définie par EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL. Avec SQLite, le fichier réutilise également les pages libérées au lieu de les rendre au système de fichiers. Sa taille sur disque reste donc stable pendant un certain temps après la suppression des lignes. Définissez EXECUTIONS_DATA_MAX_AGE et EXECUTIONS_DATA_PRUNE_MAX_COUNT selon les capacités de votre serveur, puis vérifiez le résultat le lendemain plutôt qu’immédiatement.

Pourquoi mon workflow planifié ne s’est-il pas exécuté pendant le redémarrage de n8n ?

n8n enregistre les triggers au démarrage du processus et ne rejoue pas les planifications arrivées à échéance pendant son arrêt. Une boucle de redémarrage produit donc un silence, et non une série d’exécutions de rattrapage. L’exécution suivante a lieu à la prochaine échéance après le démarrage. Si certaines exécutions ne peuvent pas être manquées, déclenchez le workflow depuis un appelant externe qui appelle un webhook. La logique de nouvelle tentative reste ainsi en dehors de n8n.

Un healthcheck redémarrera-t-il n8n lorsqu’il ne répond plus ?

Non, pas à lui seul. Un healthcheck Compose indique seulement si le conteneur est healthy ou unhealthy. Le redémarrage relève de la restart policy. restart: unless-stopped redémarre donc le conteneur après son arrêt, et également après le redémarrage de l’hôte si le service Docker est activé. Confirmez ce point avec sudo systemctl is-enabled docker. Pour agir spécifiquement lorsqu’un conteneur est unhealthy, vous devez utiliser un watcher externe à Docker qui lit son état et redémarre le service.