N8n qui ralentit au fil du temps (40k+ exécutions/mois)

Décrivez le problème/l’erreur/la question

Nous exécutons une instance n8n auto-hébergée sur Railway et rencontrons des problèmes de performance croissants à mesure que notre utilisation augmente.
Nous traitons actuellement plus de 40 000 exécutions de flux de travail par mois.
Le problème principal est que les flux de travail qui devraient normalement s’exécuter en environ 1 seconde prennent maintenant fréquemment 3 à 4 secondes, même si la logique du flux de travail elle-même n’a pas changé.
Nous voyons également des files d’attente d’exécution se former de manière aléatoire. Cela ne devrait pas se produire selon notre configuration actuelle, car nous n’avons pas intentionnellement configuré de limites de concurrence.
Plus récemment, nous avons également commencé à voir cette erreur :
Cette exécution n’a pas pu être traitée trop de fois et ne sera plus relancée. Pour permettre à cette exécution de se terminer, veuillez décomposer votre flux de travail ou augmenter la capacité de vos workers ou ajuster vos paramètres de worker.

Nous ne savons honnêtement pas quoi d’autre essayer. Les métriques Railway pour nos workers, instance principale et PostgreSQL semblent tous sains, sans goulots d’étranglement de ressources évidents. Quelqu’un a-t-il rencontré un problème similaire ou a-t-il des idées sur ce que nous devrions investiguer ensuite ?

Quel est le message d’erreur (le cas échéant) ?

Veuillez partager votre flux de travail

(Sélectionnez les nœuds sur votre canevas et utilisez les raccourcis clavier CMD+C/CTRL+C et CMD+V/CTRL+V pour copier et coller le flux de travail.)

Partagez la sortie renvoyée par le dernier nœud

Informations sur votre configuration n8n

  • Version n8n : 2.28.4
  • Base de données : PostgreSQL
  • Paramètre n8n EXECUTIONS_PROCESS (par défaut : own, main) :
  • Exécution de n8n via (Docker, npm, n8n cloud, application de bureau) : railway
  • Système d’exploitation : Windows 11

Bonjour @jptg

En fonction des symptômes et du message d’erreur que vous voyez, votre instance n8n souffre probablement d’une surcharge de processus et d’un gonflement de la base de données, ce qui est courant quand les instances auto-hébergées approchent 40 000+ exécutions par mois.

L’erreur « This execution failed to be processed too many times » se produit généralement quand une exécution est récupérée par un worker, mais que le worker échoue à « se signaler » ou à se terminer avant l’expiration du verrou. Le système suppose que le worker s’est écrasé et relance le travail jusqu’à atteindre la limite.

Vous avez mentionné le paramètre EXECUTIONS_PROCESS. Si vous utilisez le mode par défaut own, n8n lance un tout nouveau processus Node.js pour chaque exécution.

  • Le problème : Cela ajoute une surcharge importante (CPU et RAM) et crée un délai de « démarrage à froid » de 1 à 3 secondes pour chaque workflow. À mesure que votre utilisation augmente, cela exerce une énorme pression sur le planificateur du système d’exploitation et la mémoire.
  • La solution : Modifiez votre variable d’environnement pour : EXECUTIONS_PROCESS=main

Si vous exécutez n8n en mode Queue (avec des workers séparés), vous atteignez probablement un délai d’expiration du verrou. Même si un workflow ne prend que 4 secondes, la contention de la base de données ou les variations du réseau sur Railway peuvent causer l’échec du « heartbeat ».

  • La solution : Augmentez la durée du verrou pour donner plus de marge aux workers. Ajoutez cette variable d’environnement : QUEUE_WORKER_LOCK_DURATION=120000 (Cela augmente le verrou de 60 s à 120 s).

Les métriques de Railway affichent le CPU/RAM, mais ne montrent pas le gonflement des tables PostgreSQL. Avec 40 000+ exécutions/mois, votre table execution_entity peut devenir énorme, ralentissant les requêtes mêmes que n8n utilise pour gérer la queue.

  • L’investigation : Vérifiez si vous avez activé l’élagage des exécutions. Si la base de données est gonflée, même des métriques CPU « saines » ne vous sauveront pas des I/O lents.
  • La solution : Assurez-vous que ces variables sont définies pour garder votre base de données en bon état :
    • EXECUTIONS_DATA_PRUNE=true
    • EXECUTIONS_DATA_MAX_AGE=168 (Élague les données plus anciennes que 7 jours ; ajustez selon vos besoins).
    • EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000 (Limite le nombre total d’enregistrements).

Comme vous n’avez pas défini de limites de concurrence, une rafale soudaine de webhooks peut déclencher des dizaines de processus simultanés, causant les « queues aléatoires » et les baisses de performance quand le système se paralyse.

  • La solution : Définissez un plafond global pour protéger votre instance : N8N_CONCURRENCY_PRODUCTION_LIMIT=10 (Commencez avec 10 et augmentez si vos ressources Railway le permettent).

Salut, merci pour ta réponse détaillée !

Nous fonctionnons déjà en Mode Queue, et nous avons maintenant appliqué presque toutes tes suggestions.

  • Nous avions déjà le Mode Queue configuré.
  • Nous avons activé l’élagage des exécutions (EXECUTIONS_DATA_PRUNE, EXECUTIONS_DATA_MAX_AGE, etc.).
  • Nous avons également augmenté la durée du verrou du worker.

La seule suggestion que nous n’avons pas appliquée était EXECUTIONS_PROCESS=main, car d’après ce que nous comprenons, ce paramètre est obsolète dans les versions récentes de n8n et n’est pas applicable lors de l’utilisation du Mode Queue.

Pour le moment, nous n’avons pas eu l’occasion de valider si ces modifications ont amélioré la situation, car la dégradation des performances se produit de façon intermittente. Nous attendons le prochain incident pour voir si le problème réapparaît.

Pour ma part, je vais exécuter plusieurs instances de n8n pour éviter tout ça complètement.

Dans mon dernier poste, j’avais une instance pour chaque département de l’entreprise

Service
Opérations
Marketing
RH
Développement (mon cimetière de workflows)

ce qui m’a aussi poussé à créer une plateforme de gestion pour eux lol

Vous avez en fait fait les bonnes choses — mode queue, pruning, le lock bump — et vous avez raison que EXECUTIONS_PROCESS est déprécié (le mode own-process a été supprimé ; sur n8n moderne, c’est tout main ou queue/worker, donc celui-ci était un no-op pour vous). Le hic, c’est que vous avez changé quatre variables à la fois sans référence de base, donc même si ça s’est amélioré, vous ne pouvez pas savoir quel bouton l’a fait. Avant d’en tourner d’autres, mesurez — voici comment savoir lequel des trois goulots d’étranglement habituels vous avez vraiment : I/O DB, contention worker, ou un seul workflow qui fuit.

L’activation du pruning ne récupère pas ce qui est déjà là. EXECUTIONS_DATA_PRUNE arrête seulement les nouvelles lignes qui s’accumulent au-delà de votre seuil à l’avenir — cela ne réduit pas une table déjà gonflée. Dans Postgres, les lignes supprimées deviennent des dead tuples, et la taille sur disque de execution_data (la table payload — la grosse, pas execution_entity) plus ses index restent grandes jusqu’à ce qu’elles soient aspirées, et l’autovacuum ne peut souvent pas suivre sur une table qui a déjà grossi. Donc « nous avons activé le pruning et rien n’a changé » est exactement ce à quoi on s’attendrait si votre lenteur est l’I/O DB. Vérifiez-le directement :

  • SELECT relname, n_live_tup, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC; — si execution_data a des millions de dead tuples et last_autovacuum est ancien ou null, c’est votre réponse.
  • SELECT pg_size_pretty(pg_total_relation_size('execution_data')); — si la taille physique est énorme, le pruning à l’avenir ne servira à rien ; vous avez besoin d’un pg_repack (s’exécute en ligne, pas de long lock) pour vraiment le récupérer. VACUUM FULL fonctionne aussi mais verrouille la table.
  • Votre message d’erreur est un signal spécifique, pas une lenteur générale. « Cette exécution n’a pas pu être traitée trop de fois » est le chemin du travail bloqué : un worker a loué l’exécution, n’a pas terminé ou n’a pas fait de heartbeat avant l’expiration du verrouillage, elle a été remise en file d’attente, et après N tentatives elle est marquée comme échouée. Votre lock bump ne vous aide que si la cause est vraiment des exécutions longues. Celui qui mord les gens sur Railway et se cache derrière « CPU sain » est un worker qui atteint sa limite mémoire — Railway redémarre silencieusement un conteneur qui dépasse son plafond RAM, et chaque exécution en vol sur ce worker lance exactement cette erreur. Le % CPU semble bon parce que l’arrêt est mémoire, pas CPU. Regardez le nombre de redémarrages du service worker et le graphique mémoire (pas CPU) et vérifiez si les redémarrages s’alignent avec les défaillances.

Une de plus, si un workflow déplace des fichiers. À 40k/mois, si vous gérez des PDF/images et que le mode binary data est encore par défaut, c’est une source de bloat mémoire + DB, et c’est aussi peu fiable sur plusieurs workers (le fichier atterrit sur le disque local d’un worker). N8N_DEFAULT_BINARY_DATA_MODE=s3 si c’est le cas — ignorez si vous êtes tout-JSON.

L’ordre dans lequel j’irais : les deux requêtes Postgres en premier (règle DB dedans ou dehors en environ une minute), puis le graphique restart/memory du worker (règle OOM dehors), puis changez une seule chose et observez un seul nombre pour que le prochain tour soit vraiment mesurable.

Si c’est utile : épingler lequel de ces éléments est vraiment votre goulot d’étranglement à partir de votre vrai output pg_stat et de vos métriques worker est le genre de démontage que je fais comme diagnostic écrit fixe de 49 $ — vous envoyez les résultats de requête assainis + le graphique mémoire du worker, je renvoie une cause racine et une liste de corrections hiérarchisée, async, pas d’appel. Si vous voulez ensuite le surveiller continuellement — alertes sur les redémarrages OOM worker et queue-wait avant qu’ils ne se transforment en exécutions échouées — c’est une configuration de surveillance de 149 $/mois. Mais lancez d’abord ces deux requêtes Postgres ; si c’est juste du bloat accumulé, un pg_repack le corrige et vous n’aurez pas besoin de moi.

S’il voulait des réponses basées sur l’IA, il aurait utilisé Claude, ChatGPT, etc.

Un suivi avec un point que j’aurais dû inclure la première fois, car c’est ce qui fait que les conseils de pruning semblent ne pas avoir fonctionné.

Si vous avez exécuté les deux requêtes pg_stat et que execution_data est toujours énorme, vérifiez si les lignes sont réellement supprimées ou seulement marquées comme supprimées avant de conclure que le pruning est cassé. Le pruning de n8n effectue la suppression par étapes, il y a donc une fenêtre où les exécutions sont marquées pour suppression mais les lignes de payload sont toujours physiquement présentes, et sur une instance occupée qui a redémarré, le backlog marqué peut y rester indéfiniment. Comparer le nombre de lignes dans execution_entity avec ce que l’interface utilisateur vous montre comme exécutions existantes est généralement suffisant pour savoir dans quelle situation vous vous trouvez, et cela change complètement la correction : si les lignes sont déjà supprimées, vous avez un ballonnement de tuples morts et avez besoin d’une reconstruction, et si elles sont toujours là, aucun vacuum ne vous aidera jusqu’à ce qu’elles soient réellement supprimées.

La deuxième moitié compte spécifiquement sur Railway. pg_repack reconstruit la table à côté de l’originale avant l’échange, il a donc besoin d’espace disque à peu près égal à la taille de la table et de ses index. Si execution_data représente la plupart du volume, la reconstruction s’exécutera pendant un moment puis échouera sur disque, et vous serez dans une pire position qu’au départ. Vérifiez d’abord l’espace libre par rapport à pg_total_relation_size. S’il n’y a pas assez de marge, la route moins coûteuse est de réduire considérablement le nombre de lignes d’abord, par lots pour ne pas maintenir une seule transaction énorme, et seulement ensuite récupérer l’espace.

Cela vaut la peine de le dire clairement : si les deux requêtes pointaient vers le worker plutôt que vers la base de données, rien de ce qui précède n’est votre problème et je l’ignorerais.