Por que o n8n fica offline no seu VPS
O aviso pode ser websocket, loop de restart, OOM kill ou agenda parada. Veja como distinguir cada falha no Docker, Node.js e reverse proxy.
Por que o n8n fica offline: quatro falhas, um sintoma
“o n8n fica offline” é uma frase que pode descrever quatro falhas diferentes, e cada uma exige uma correção diferente. O editor mostra um aviso de ligação perdida enquanto o contentor continua a funcionar normalmente. O contentor reinicia sozinho. O kernel termina o processo Node.js por utilizar demasiada memória. Ou não existe qualquer problema com o processo, e um workflow ativo simplesmente nunca é executado. Se alterar a definição errada, passará um fim de semana a investigar um problema que nunca existiu.
Por isso, descubra qual é a falha antes de alterar qualquer configuração. O n8n é executado como um único processo Node.js, normalmente dentro de um contentor Docker, atrás de um reverse proxy que termina o TLS (transport layer security). Cada uma dessas camadas falha de forma diferente, mas o browser apresenta a mesma mensagem para todas elas.
Diagnostique nesta ordem
Execute estes comandos no VPS (virtual private server) e leia os valores apresentados pela sua própria máquina. Não os compare com números de uma publicação num fórum. Os valores relevantes aqui descrevem o seu servidor, não o de outra pessoa.
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-streamA coluna STATUS de docker ps -a indica há quanto tempo o contentor está no estado atual. Compare esse valor com o momento em que o problema começou. Se o contentor estiver ativo desde muito antes de o banner aparecer, o n8n nunca ficou indisponível. O que falhou foi a ligação entre o browser e o backend. O caminho websocket correspondente é explicado na secção seguinte.
RestartCount indica quantas vezes o Docker reiniciou este contentor. Anote o número, aguarde um minuto e leia-o novamente. Se o número aumentar enquanto observa, existe um ciclo de reinício. As linhas do log imediatamente anteriores a cada reinício indicam a causa.
OOMKilled é um sinalizador com valor true ou false. True significa que o kernel Linux terminou o processo porque este ultrapassou um limite de memória, seja o limite do próprio contentor ou o da máquina inteira. Esse campo distingue um encerramento por falta de memória de qualquer outro tipo de saída. Por isso, leia-o antes de avançar para suposições.
ExitCode indica o código com que o contentor terminou da última vez. Não precisa de memorizar o significado de cada código. Leia o seu valor e, em seguida, leia o final de docker logs correspondente ao mesmo momento. O final do log e o sinalizador de falta de memória, em conjunto, indicam o que aconteceu. Isoladamente, qualquer um deles pode induzir em erro.
docker stats apresenta a utilização atual de memória junto ao limite em vigor. Deixe este comando em execução num segundo terminal, execute o workflow que causa o problema e observe o comportamento do valor enquanto a falha ocorre.
O banner de perda de ligação normalmente é causado pelo reverse proxy
O editor do n8n mantém uma ligação push de longa duração aberta com o backend para transmitir o progresso da execução para a tela. Por padrão, essa ligação é um WebSocket, selecionado por N8N_PUSH_BACKEND, e o valor padrão é websocket. Um WebSocket começa como um pedido HTTP normal que contém os cabeçalhos Connection: Upgrade e Upgrade: websocket. O servidor responde 101 Switching Protocols e, a partir desse momento, os dois lados utilizam o mesmo socket TCP nos dois sentidos.
Duas situações interrompem esse funcionamento, e ambas ocorrem no proxy, não no n8n. O proxy comunica com o upstream usando HTTP/1.0 ou remove os cabeçalhos de upgrade. Nesse caso, o upgrade nunca ocorre e o editor tenta restabelecer a ligação continuamente. Ou o upgrade é concluído, mas o proxy fecha o socket mais tarde por falta de atividade, porque um WebSocket sem mensagens tem exatamente o aspeto de uma ligação inativa. Nos dois casos, o contentor está saudável. O banner indica que o browser perdeu o canal.
Confirme isto no browser antes de editar qualquer configuração. Abra as ferramentas de desenvolvimento, aceda ao separador Network, filtre por WS e recarregue o editor. O pedido push deve chegar a 101 Switching Protocols e permanecer aberto. Um pedido push que devolve um código de estado normal ou que reaparece a cada poucos segundos aponta para o proxy.
Configurações do nginx que mantêm o editor conectado
O nginx não encaminha uma atualização sem que isso seja configurado. proxy_pass usa HTTP/1.0 para comunicar com o backend por predefinição, e Connection e Upgrade são cabeçalhos hop-by-hop que o nginx remove durante o encaminhamento. É necessário repor ambos. O bloco map deve ficar no contexto http, e não dentro de server. Se o restante do bloco de servidor abaixo não for familiar, o guia linha a linha de um bloco de servidor do nginx explica o que cada diretiva faz.
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 é a linha que costuma ficar de fora. O valor predefinido é 60 segundos e também se aplica a um WebSocket atualizado. Por isso, um separador do editor deixado aberto numa instância sem atividade perde a ligação cerca de um minuto depois da última mensagem passar por ele. Aumentar este valor corrige o banner que aparece quando regressa a um separador deixado aberto.
sudo nginx -t && sudo systemctl reload nginx
sudo nginx -T | grep -iE 'proxy_http_version|upgrade|proxy_read_timeout'nginx -T apresenta toda a configuração em execução, em vez de apenas um ficheiro. Assim, confirma que a alteração foi efetivamente carregada. Se nenhum ficheiro incluído por uma linha include contiver a configuração, uma correção correta parecerá não fazer nada.
Depois, informe o n8n de que está atrás de um proxy, porque ele cria URLs com base nestes valores.
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 tem o valor predefinido 0. Isso faz com que o n8n trate o endereço de ligação como o endereço do cliente e ignore X-Forwarded-For. Defina-o como o número de proxies à frente do contentor. Em agosto de 2026, N8N_WEBHOOK_URL é o nome atual, e o nome antigo WEBHOOK_URL continua a funcionar, embora apresente um aviso de descontinuação no arranque.
O Traefik encaminha WebSockets, mas termina a ligação por timeout
O Traefik encaminha uma atualização para WebSocket sem middleware nem labels adicionais. Por isso, quando um utilizador do Traefik vê este banner, normalmente está a atingir um timeout, e não a lidar com um cabeçalho em falta. Os parâmetros estão no entryPoint. Em agosto de 2026, no Traefik v3, idleTimeout tem o valor predefinido de 180 segundos e readTimeout tem o valor predefinido de 60 segundos.
entryPoints:
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: 0
idleTimeout: 3600sO Caddy trata automaticamente da atualização em reverse_proxy e não precisa de nenhuma diretiva para isso. Se não puder alterar o proxy porque outra pessoa o administra, altere o canal de push com N8N_PUSH_BACKEND=sse. Os SSE (server-sent events) são uma resposta HTTP normal mantida aberta. Por isso, funcionam através de um proxy que recusa atualizações, embora um timeout de inatividade agressivo também possa interrompê-los. Escolher o próprio proxy é uma decisão separada. A comparação entre nginx, Caddy e Traefik explica o custo operacional de cada opção.
Quando o container está realmente a reiniciar
Se RestartCount aumentar, o container está a falhar e o Docker está a iniciá-lo novamente. Compare os timestamps dos logs com cada reinício e leia o que aconteceu imediatamente antes. Quatro causas abrangem quase todos os casos: um erro de configuração que impede o arranque, uma base de dados à qual o n8n não consegue aceder, uma falha depois de o processo arrancar e uma terminação por falta de memória.
Comece pelo volume, porque as permissões são a causa menos evidente. A imagem oficial é executada com o utilizador sem privilégios node e guarda os dados em /home/node/.n8n. Um bind mount criado por root não pode ser escrito por esse utilizador. Por isso, o processo termina no arranque todas as vezes, e a política de reinício oculta o problema num ciclo.
docker compose config
docker run --rm -it --entrypoint sh docker.n8n.io/n8nio/n8n -c 'id'
docker exec n8n ls -ld /home/node/.n8nUm volume nomeado evita completamente o problema, porque o Docker cria-o com o proprietário correto. Se precisar de um bind mount, chown o diretório do host para o ID numérico de utilizador apresentado pelo primeiro comando. Vale a pena compreender uma vez o mapeamento de proprietários entre o host e o container. O guia sobre PUID e PGID explica como estas imagens determinam quem pode escrever nos ficheiros.
O encerramento por falta de memória que parece uma falha
Existem dois limites de memória independentes acima de um processo n8n, e cada um falha de forma diferente. O limite do grupo de controlo do contentor é aplicado pelo kernel: quando é ultrapassado, o processo é encerrado imediatamente, sem possibilidade de escrever qualquer informação, e OOMKilled devolve true. O limite do heap do V8 é aplicado dentro do Node.js: quando é ultrapassado, o Node gera um erro de heap com um stack trace e termina por iniciativa própria, pelo que OOMKilled devolve false. No browser, estes casos parecem idênticos. Em docker inspect, ficam separados por um único campo.
Defina o limite do heap do Node abaixo do limite do contentor. Se o limite do heap for o mais elevado dos dois, o V8 continua a alocar memória depois do ponto em que o kernel intervém. Assim, o garbage collector nunca atinge o seu próprio limite e ocorre sempre a falha mais grave, sem logs para consultar.
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>Escolha ambos os valores com base nos recursos efetivamente disponíveis na VPS e reserve memória para a base de dados, o proxy e o sistema operativo. docker stats --no-stream apresenta a utilização atual junto ao limite em vigor, para que possa confirmar que o limite definido é o limite aplicado pelo Docker. Como são aplicados os limites de memória do Compose explica qual chave prevalece quando existem várias configurações.
Os dados das execuções acumulam-se por baixo do sistema
Uma única execução contém a saída de todos os nós enquanto decorre, e o n8n armazena esses dados. Daqui resultam duas consequências. O pico de memória de uma execução é determinado pelo maior lote de dados que circula pelo workflow. Por isso, um workflow que processa dez mil linhas de uma vez é um programa diferente do mesmo workflow a processar duzentas de cada vez. Além disso, a cópia armazenada continua a crescer até ser eliminada.
A limpeza resolve o segundo problema. Em agosto de 2026, as predefinições são a limpeza ativada, EXECUTIONS_DATA_MAX_AGE em 336 horas (14 dias) e EXECUTIONS_DATA_PRUNE_MAX_COUNT em 10000. Estes valores são elevados para um VPS pequeno com SQLite, onde um único ficheiro contém tudo e o mesmo processo que disponibiliza o editor tem de o ler e escrever.
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=falseEXECUTIONS_DATA_SAVE_ON_SUCCESS=none é a definição agressiva. Mantém as execuções falhadas para depuração e elimina as concluídas com sucesso. Tome esta decisão de forma consciente, porque um workflow que produza uma saída incorreta sem gerar um erro não deixará nada para inspecionar. A limpeza também marca primeiro as linhas como eliminadas e remove-as numa passagem posterior. Além disso, o SQLite reutiliza as páginas libertadas em vez de as devolver ao sistema, pelo que o ficheiro no disco não diminui imediatamente depois de alterar a definição.
Para reduzir o pico em vez do total armazenado, mova menos dados por execução. Divida os trabalhos grandes em sub-workflows que devolvam resultados pequenos ao workflow principal, use o nó Loop Over Items para processar lotes e mantenha datasets completos fora do nó Code.
Ficheiros binários não devem passar pela memória
N8N_DEFAULT_BINARY_DATA_MODE assume default por predefinição. Esta configuração mantém os dados binários na memória da execução em curso. Cada ficheiro transferido por um nó e cada cópia entregue ao nó seguinte permanecem na memória até à execução terminar. Um único workflow que transfira alguns anexos grandes pode fazer o processo ultrapassar um limite que as operações normais com JSON nunca atingem. Por isso, a falha ocorre num workflow específico e não após um determinado período.
environment:
- N8N_DEFAULT_BINARY_DATA_MODE=filesystemCom filesystem, os dados binários são gravados em N8N_BINARY_DATA_STORAGE_PATH. Por predefinição, este caminho fica dentro da pasta do utilizador do n8n e, portanto, no mesmo volume que o restante conteúdo. Confirme se o volume tem espaço disponível antes de ativar esta opção. N8N_PAYLOAD_SIZE_MAX define o tamanho máximo do payload recebido por webhook, em MiB (mebibytes), e assume 16 por predefinição. Aumentar este valor permite receber pedidos maiores, mas implica aceitar o custo adicional na memória.
Tudo o que partilha o servidor concorre pela mesma RAM. Se os processos OOM kill começaram depois de adicionar um contentor de base de dados, executar a base de dados no Docker ou no host é a escolha que está agora a fazer.
Política de reinício e recuperação após um reboot
Um contentor sem uma política de reinício permanece parado depois de terminar e também depois de o host reiniciar. restart: unless-stopped volta a iniciá-lo nos dois casos, mas respeita um contentor que tenha sido parado manualmente. restart: always também reinicia um contentor parado deliberadamente quando o Docker iniciar novamente.
O n8n disponibiliza um endpoint de saúde, identificado por N8N_ENDPOINT_HEALTH, que usa healthz por predefinição. Consulte-o primeiro a partir do host para confirmar que o caminho está correto na sua instância.
curl -fsS http://127.0.0.1:5678/healthz
docker exec n8n which wget curl
sudo systemctl is-enabled dockerUm healthcheck, por si só, não reinicia nada. O Compose marca o contentor como não saudável e não faz mais nada. Por isso, o healthcheck precisa de uma política de reinício ou de um monitor externo ao lado dele para produzir algum efeito. Escrever um healthcheck que realmente atua e fazer a stack iniciar novamente depois de um reboot abrangem as duas partes.
O workflow que nunca é executado embora o n8n esteja operacional
Este workflow não apresenta nenhum banner nem reinicia. O contentor está em execução, o editor funciona e a execução esperada não aparece na lista de execuções. Quatro causas explicam a maioria destes casos.
- O workflow não está ativo. Um Schedule Trigger só é executado no percurso de produção. Testá-lo no canvas não agenda nada.
- O fuso horário não é o seu.
GENERIC_TIMEZONEusaAmerica/New_Yorkpor predefinição. Por isso, um agendamento definido para 09:00 é executado às 09:00 nesse fuso até definirGENERIC_TIMEZONEeTZcom o seu fuso horário. - O período de indisponibilidade não é compensado depois. Os triggers são registados quando o n8n arranca. Por isso, um agendamento que venceu enquanto o contentor reiniciava não é executado com atraso. A próxima execução ocorre no próximo horário previsto depois do arranque.
- O workflow foi desativado automaticamente.
N8N_WORKFLOW_AUTODEACTIVATION_ENABLEDestá desativado por predefinição. Quando está ativo, um workflow que continua a falhar é retirado de publicação. Depois disso, parece exatamente um workflow que nunca foi ativado.
Abra a lista de execuções e filtre pelo workflow. Uma entrada com falha indica um problema no workflow. Se a falha foi um erro 429 contra outro serviço alojado no mesmo servidor, o limite pertence a esse serviço, não ao n8n. O guia passo a passo sobre erros 429 no SearXNG mostra como distinguir o próprio limitador de taxa dos motores que estão a bloquear o IP do seu servidor. A ausência total de entradas indica um problema no trigger. É nesses quatro pontos que deve procurar.
O que alterar primeiro
- Leia
STATUS,RestartCounteOOMKilledno seu próprio contentor antes de editar qualquer ficheiro. - Se o contentor nunca parou, corrija os cabeçalhos de atualização do proxy e o tempo limite de inatividade.
- Se
OOMKilledfor verdadeiro, defina deliberadamente um limite para o contentor, coloque o limite máximo de memória do Node abaixo desse valor e altere os dados binários parafilesystem. - Se nada foi acionado, confirme se o workflow está ativo e se o fuso horário da instância é o seu.
A maior parte disto é configuração que se define uma vez e depois não precisa de manutenção, sobre uma instalação funcional. Se ainda estiver a montar essa instalação, o guia do n8n no Docker com HTTPS é a base a que estas definições pertencem.
FAQ
Por que o editor do n8n mostra um aviso de ligação perdida quando o contentor está em execução?
O editor mantém um WebSocket aberto para transmitir o progresso da execução. Se o reverse proxy não encaminhar os cabeçalhos Connection: Upgrade e Upgrade: websocket, ou não usar HTTP/1.1 no upstream, a atualização nunca é concluída e o navegador tenta ligar-se novamente indefinidamente, enquanto o n8n continua saudável. No nginx, precisa de proxy_http_version 1.1 e das duas linhas proxy_set_header, além de um proxy_read_timeout superior aos 60 segundos predefinidos, para que um separador inativo não seja desligado. Verifique a configuração em execução com sudo nginx -T, e não com o ficheiro que editou.
Como distingo uma eliminação por falta de memória de uma falha normal?
Execute docker inspect n8n | grep -iE 'OOMKilled|ExitCode|RestartCount' e leia a flag OOMKilled. True significa que o kernel eliminou o processo por ultrapassar um limite de memória. Não haverá informação útil no log do contentor, porque o processo não teve oportunidade de escrever. False, juntamente com um erro de heap e um stack trace no fim de docker logs, significa que o Node.js atingiu o seu próprio limite de heap V8 e terminou por iniciativa própria. Defina NODE_OPTIONS=--max-old-space-size abaixo do limite do contentor para obter a segunda falha, que é a que deixa evidências.
A limpeza dos dados de execução liberta espaço em disco imediatamente?
Não. EXECUTIONS_DATA_PRUNE marca as execuções antigas para eliminação e uma passagem posterior remove-as, de acordo com o agendamento definido por EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL. Com SQLite, o ficheiro também reutiliza as páginas libertadas em vez de as devolver ao sistema de ficheiros. Por isso, o tamanho em disco mantém-se estável durante algum tempo depois de as linhas serem removidas. Defina EXECUTIONS_DATA_MAX_AGE e EXECUTIONS_DATA_PRUNE_MAX_COUNT com valores adequados ao seu servidor. Volte a verificar no dia seguinte, e não imediatamente.
Por que motivo o meu workflow agendado não foi executado enquanto o n8n estava a reiniciar?
O n8n regista os triggers quando o processo arranca. Não repõe os agendamentos cujo prazo ocorreu enquanto estava parado. Por isso, um ciclo de reinícios produz silêncio em vez de várias execuções de recuperação. A execução seguinte ocorre no próximo horário devido depois do arranque. Se precisar de execuções que não possam ser perdidas, inicie o workflow a partir de um sistema externo que invoque um webhook. Assim, a lógica de novas tentativas fica fora do n8n.
Um healthcheck reinicia o n8n quando este deixa de responder?
Não, por si só. Um healthcheck do Compose apenas marca o contentor como saudável ou não saudável. O reinício é responsabilidade da política de reinício. Por isso, restart: unless-stopped repõe o contentor depois de este terminar e também o repõe depois de um reboot do host, desde que o serviço Docker esteja ativado. Confirme isso com sudo systemctl is-enabled docker. Para agir especificamente quando o contentor fica não saudável, precisa de um watcher externo ao Docker que leia o estado e reinicie o serviço.