Estamos executando uma instância n8n auto-hospedada no Railway e temos enfrentado problemas crescentes de desempenho conforme nosso uso aumenta.
Atualmente processamos mais de 40.000 execuções de fluxo de trabalho por mês.
O principal problema é que fluxos de trabalho que normalmente deveriam ser concluídos em cerca de 1 segundo agora frequentemente levam 3–4 segundos para terminar, mesmo que a lógica do fluxo de trabalho em si não tenha mudado.
Também estamos vendo filas de execução se formando aleatoriamente. Isso não deveria acontecer com base na nossa configuração atual, já que não configuramos intencionalmente nenhum limite de concorrência.
Mais recentemente, também começamos a ver este erro:
Esta execução falhou em ser processada muitas vezes e não será mais repetida. Para permitir que esta execução seja concluída, divida seu fluxo de trabalho ou aumente seus workers ou ajuste suas configurações de worker.
Honestamente, não sabemos o que mais tentar. As métricas do Railway para nossos workers, instância primária e PostgreSQL parecem todas saudáveis, sem gargalos óbvios de recursos. Alguém já experimentou algo semelhante ou tem alguma ideia sobre o que devemos investigar a seguir?
Qual é a mensagem de erro (se houver)?
Compartilhe seu fluxo de trabalho
(Selecione os nós em sua tela e use os atalhos de teclado CMD+C/CTRL+C e CMD+V/CTRL+V para copiar e colar o fluxo de trabalho.)
Com base nos sintomas e na mensagem de erro que você está vendo, sua instância n8n provavelmente está sofrendo com Sobrecarga de Processo e Inchaço de Banco de Dados, que são comuns conforme instâncias auto-hospedadas crescem em direção a 40k+ execuções por mês.
O erro “Esta execução falhou em ser processada muitas vezes” normalmente ocorre quando uma execução é pega por um worker, mas o worker falha em “fazer check-in” ou terminar antes do lock expirar. O sistema assume que o worker travou e retenta o job até atingir o limite.
Você mencionou a configuração EXECUTIONS_PROCESS. Se você está usando o modo padrão own, n8n spawna um novo processo Node.js para cada execução.
O Problema: Isso adiciona overhead significativo (CPU e RAM) e cria um atraso de “cold start” de 1–3 segundos para cada workflow. Conforme seu uso cresce, isso coloca imensa pressão no scheduler do SO e na memória.
A Solução: Mude sua variável de ambiente para: EXECUTIONS_PROCESS=main
Se você está rodando n8n em Queue Mode (usando workers separados), você provavelmente está batendo em um timeout de lock. Mesmo que um workflow leve apenas 4 segundos, contenção de banco de dados ou jitter de rede no Railway pode causar falha no “heartbeat”.
A Solução: Aumente a duração do lock para dar mais espaço aos workers. Adicione esta variável de ambiente: QUEUE_WORKER_LOCK_DURATION=120000 (Isso aumenta o lock de 60s para 120s).
As métricas do Railway mostram CPU/RAM, mas não mostram inchaço de tabela PostgreSQL. Com 40.000+ execuções/mês, sua tabela execution_entity pode crescer enormemente, desacelerando as mesmas queries que n8n usa para gerenciar a fila.
A Investigação: Verifique se você tem pruning de execução ativado. Se o banco de dados está inchado, nem mesmo métricas de CPU “saudáveis” vão te salvar de I/O lento.
A Solução: Certifique-se de que estas variáveis estão configuradas para manter seu banco de dados enxuto:
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168 (Prune dados com mais de 7 dias; ajuste conforme necessário).
EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000 (Limita o total de registros).
Como você não definiu limites de concorrência, uma explosão repentina de webhooks pode disparar dezenas de processos simultâneos, causando “filas aleatórias” e quedas de performance conforme o sistema se debate.
A Solução: Defina um teto global para proteger sua instância: N8N_CONCURRENCY_PRODUCTION_LIMIT=10 (Comece com 10 e aumente se seus recursos no Railway permitirem).
Já estamos rodando em Queue Mode, e aplicamos quase todas as suas sugestões agora.
Já tínhamos o Queue Mode configurado.
Habilitamos a limpeza de execuções (EXECUTIONS_DATA_PRUNE, EXECUTIONS_DATA_MAX_AGE, etc.).
Também aumentamos a duração do lock do worker.
A única sugestão que não aplicamos foi EXECUTIONS_PROCESS=main, porque pelo que entendemos essa configuração foi descontinuada nas versões recentes do n8n e não é aplicável quando se usa Queue Mode.
No momento ainda não tivemos a chance de validar se essas mudanças melhoraram a situação, já que a degradação de performance acontece intermitentemente. Estamos aguardando o próximo incidente para ver se o problema reaparece.
Você realmente fez as coisas certas — modo de fila, limpeza, o bump de lock — e está certo que EXECUTIONS_PROCESS está descontinuado (o modo own-process foi removido; no n8n moderno é tudo main ou queue/worker, então aquele era um no-op para você). O problema é que você alterou quatro variáveis de uma vez sem uma linha de base, então mesmo que tenha melhorado, você não consegue saber qual dos botões fez. Antes de mexer mais, meça — aqui está como saber qual dos três gargalos usuais você realmente tem: I/O de BD, contenção de worker ou um único workflow vazando.
Ativar limpeza não recupera o que já está lá.EXECUTIONS_DATA_PRUNE apenas impede que novas linhas se acumulem além do seu limite a partir de agora — não encolhe uma tabela que já está inchada. No Postgres, as linhas deletadas se tornam tuplas mortas, e o tamanho em disco de execution_data (a tabela de payload — a grande, não execution_entity) mais seus índices permanece grande até ser aspirado, e o autovacuum frequentemente não consegue acompanhar uma tabela que já ficou enorme. Então “ativamos limpeza e nada mudou” é exatamente o que você esperaria se sua lentidão for I/O de BD. Verifique diretamente:
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC; — se execution_data tem milhões de tuplas mortas e last_autovacuum é antigo ou nulo, essa é sua resposta.
SELECT pg_size_pretty(pg_total_relation_size('execution_data')); — se o tamanho físico é enorme, limpeza a partir de agora não ajuda; você precisa de um pg_repack (executa online, sem lock longo) para realmente recuperar. VACUUM FULL também funciona, mas bloqueia a tabela.
Sua mensagem de erro é um sinal específico, não lentidão geral. “Esta execução falhou ao ser processada muitas vezes” é o caminho de job travado: um worker alugou a execução, não terminou ou fez heartbeat antes do lock expirar, ela foi re-enfileirada, e após N tentativas é marcada como falha. Seu bump de lock só ajuda se a causa for genuinamente execuções longas. A que pega as pessoas no Railway e se esconde atrás de “CPU saudável” é um worker atingindo seu limite de memória — Railway reinicia silenciosamente um contêiner que excede seu teto de RAM, e toda execução em voo naquele worker lança exatamente este erro. CPU% parece bom porque o kill é memória, não CPU. Olhe a contagem de reinicializações do serviço worker e o gráfico de memória (não CPU) e verifique se as reinicializações se alinham com as falhas.
Uma mais, se algum workflow move arquivos. Com 40k/mês, se você lida com PDFs/imagens e modo de dados binários ainda está padrão, isso é uma fonte de bloat de memória + BD, e também é pouco confiável em múltiplos workers (o arquivo cai no disco local de um worker). N8N_DEFAULT_BINARY_DATA_MODE=s3 se for o caso — ignore se for tudo JSON.
Ordem em que eu faria: as duas consultas Postgres primeiro (descarta BD ou não em cerca de um minuto), depois o gráfico de restart/memória do worker (descarta OOM), depois mude uma coisa e observe um número para que a próxima rodada seja realmente mensurável.
Se for útil: identificar qual destes é realmente seu gargalo a partir de sua saída pg_stat real e métricas de worker é o tipo de desmontagem que faço como diagnóstico escrito fixo de $49 — você envia os resultados de consulta sanitizados + o gráfico de memória do worker, eu envio de volta uma causa raiz e uma lista de fixes priorizada, assincronamente, sem chamada. Se depois você quer que seja monitorado continuamente — alertando em OOM-restarts de worker e queue-wait antes de se transformarem em execuções falhadas — isso é uma configuração de monitoramento de $149/mês. Mas execute essas duas consultas Postgres primeiro; se for apenas bloat acumulado, um pg_repack corrige e você não vai precisar de mim.
Um acompanhamento com um ponto que eu deveria ter incluído na primeira vez, porque é o que faz o conselho de pruning parecer que não funcionou.
Se você executou as duas consultas pg_stat e execution_data ainda é enorme, verifique se as linhas foram realmente removidas ou apenas marcadas como deletadas antes de concluir que o pruning está quebrado. O pruning do n8n faz a deleção em etapas, então há uma janela de tempo em que as execuções são sinalizadas para remoção, mas as linhas de payload ainda estão fisicamente presentes, e em uma instância ocupada que foi reiniciada, o backlog sinalizado pode ficar lá indefinidamente. Comparar a contagem de linhas em execution_entity com o que a interface mostra como execuções existentes geralmente é suficiente para saber em qual situação você está, e isso muda completamente o conserto: se as linhas já foram removidas, você tem bloat de tuplas mortas e precisa de um repack, e se ainda estão lá, nenhuma quantidade de vacuuming vai ajudar até que sejam realmente removidas.
A segunda metade é importante especificamente no Railway. pg_repack reconstrói a tabela ao lado do original antes de fazer a troca, então precisa de espaço em disco aproximadamente igual ao tamanho da tabela e seus índices. Se execution_data é a maior parte do volume, o repack será executado por um tempo e depois falhará no disco, e você estará em uma posição pior do que quando começou. Verifique o espaço livre em relação a pg_total_relation_size primeiro. Se não há espaço de sobra, a rota mais barata é reduzir a contagem de linhas drasticamente primeiro, em lotes para que você não esteja mantendo uma transação enorme, e somente depois reivindicar o espaço.
Vale deixar claro que se as duas consultas apontassem para o worker em vez de para o banco de dados, nada do acima seria seu problema e eu ignoraria isso.