n8n fica offline no VPS: como identificar a causa
Veja por que o n8n parece offline: banner de websocket, loop de restart, kill por falta de memoria ou workflow agendado que nao executa.
Por que o n8n fica offline: quatro falhas, um sintoma
"o n8n fica offline" é uma frase que abrange 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 memória em excesso. Ou não há qualquer problema com o processo, e um workflow ativo simplesmente nunca é executado. Altere a definição errada e passará um fim de semana a resolver um problema que nunca existiu.
Por isso, identifique 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 uma forma diferente, mas o browser comunica todas com a mesma mensagem.
Diagnostique nesta ordem
Execute estes comandos no VPS (servidor privado virtual) 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 descrevem o seu servidor, não o servidor 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. Esse é o caminho websocket descrito 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ícios. As linhas do log imediatamente anteriores a cada reinício indicam o motivo.
OOMKilled é um sinalizador com valor verdadeiro ou falso. True significa que o kernel Linux terminou o processo porque este ultrapassou um limite de memória, o limite do próprio contentor ou o limite de toda a máquina. Este campo separa um encerramento por falta de memória de qualquer outro tipo de saída. Por isso, leia-o antes de formular hipóteses.
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 fim de docker logs correspondente ao mesmo momento. O trecho final do log e o sinalizador de falta de memória, em conjunto, mostram o que aconteceu. Isoladamente, qualquer um deles pode induzir em erro.
docker stats mostra a utilização atual de memória junto ao limite em vigor. Deixe o comando em execução num segundo terminal, acione o workflow que provoca a falha e observe o comportamento do número enquanto a falha ocorre.
O banner de ligação perdida normalmente é causado pelo reverse proxy
O editor do n8n mantém uma ligação push de longa duração aberta para o backend, para poder transmitir o progresso das execuções para a tela. Por padrão, essa ligação é um WebSocket, selecionado por N8N_PUSH_BACKEND, e o valor predefinido é websocket. Um WebSocket começa como um pedido HTTP normal que inclui os cabeçalhos Connection: Upgrade e Upgrade: websocket. O servidor responde com 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 não ocorre e o editor tenta ligar-se novamente sem parar. Ou o upgrade é concluído, mas o proxy fecha o socket mais tarde por este ter ficado inativo. Um WebSocket sem mensagens parece exatamente uma ligação ociosa. 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 devolva um código de estado normal, ou que reapareça a cada poucos segundos, aponta para um problema no proxy.
As configurações do nginx que mantêm o editor ligado
O nginx não encaminha uma atualização sem que isso seja configurado. O proxy_pass comunica com o backend usando HTTP/1.0 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, não dentro de server.
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 ser esquecida. O valor predefinido é 60 segundos, e essa definição 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 de a última mensagem passar por ele. Aumentar esse valor corrige o aviso apresentado quando volta 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 mostra a configuração completa em execução, e não apenas um ficheiro. Assim, confirma que a alteração foi realmente carregada. Quando a configuração está num ficheiro que nenhuma diretiva include inclui, uma correção correta parece não produzir efeito.
Em seguida, indique ao n8n que está atrás de um proxy, porque ele constrói os URLs a partir destes valores.
environment:
- N8N_HOST=n8n.example.com
- N8N_PROTOCOL=https
- N8N_PORT=5678
- N8N_PROXY_HOPS=1
- N8N_WEBHOOK_URL=https://n8n.example.com/Por predefinição, N8N_PROXY_HOPS é 0. Isso faz com que o n8n trate o endereço da ligação como o endereço do cliente e ignore X-Forwarded-For. Defina-o como o número de proxies existentes à frente do contentor. Em agosto de 2026, N8N_WEBHOOK_URL é o nome atual, e WEBHOOK_URL continua a funcionar, embora apresente um aviso de descontinuação no arranque.
O Traefik encaminha WebSockets e depois 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. SSE (eventos enviados pelo servidor) é uma resposta HTTP normal mantida aberta. Por isso, funciona através de um proxy que recusa atualizações, embora um timeout de inatividade agressivo também possa interrompê-la. A escolha do próprio proxy é uma decisão separada. a comparação entre Nginx, Caddy e Traefik explica o custo operacional de cada um.
Quando o container está realmente reiniciando
Se RestartCount aumentar, o container está falhando e o Docker está iniciando-o novamente. Compare os horários nos logs com cada reinício e leia o que ocorreu imediatamente antes. Quatro causas abrangem quase todos os casos: um erro de configuração que impede a inicialização, um banco de dados que o n8n não consegue acessar, uma falha depois que o serviço começa a executar e uma finalização por falta de memória.
Comece pelo volume, porque as permissões são a causa mais discreta. A imagem oficial executa como o usuário sem privilégios node e mantém os dados em /home/node/.n8n. Um bind mount criado por root não pode ser gravado por esse usuário. Por isso, o processo termina na inicialização todas as vezes, e a política de reinício oculta o problema atrás de um loop.
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 esse problema, porque o Docker o cria com o proprietário correto. Se precisar usar um bind mount, chown o diretório do host para o ID numérico de usuário exibido pelo primeiro comando. Vale a pena entender uma vez o mapeamento de propriedade entre o host e o container. O explicador sobre PUID e PGID mostra como essas imagens determinam quem grava os arquivos.
O kill por falta de memória que parece uma falha
Existem dois limites de memória distintos acima de um processo n8n, e cada um falha de forma diferente. O limite do cgroup do contentor é aplicado pelo kernel: quando é ultrapassado, o processo é terminado 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 lança 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, diferem por um campo.
Defina o limite do heap do Node abaixo do limite do contentor. Se o limite do heap for o mais alto dos dois, o V8 continua a alocar memória depois do ponto em que o kernel intervém. O garbage collector nunca atinge o seu próprio limite, e obtém sempre a falha mais grave, sem qualquer log 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 reais do seu VPS, deixando margem para a base de dados, o proxy e o sistema operativo. docker stats --no-stream apresenta o uso atual junto ao limite aplicado, 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 definições.
Os dados de execução são o que cresce sem se dar conta
Uma única execução manté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 passa por ela. Por isso, um workflow que processa dez mil linhas de cada 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 automática resolve o segundo problema. Em agosto de 2026, os valores predefinidos são a limpeza ativada, EXECUTIONS_DATA_MAX_AGE definido como 336 horas (14 dias) e EXECUTIONS_DATA_PRUNE_MAX_COUNT definido como 10000. Estes valores são elevados para um VPS pequeno que utiliza SQLite. Nesse caso, um único ficheiro contém tudo, e o mesmo processo que disponibiliza o editor também tem de ler e escrever na base de dados.
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 execuções concluídas com sucesso. Tome esta decisão de forma deliberada. Se um workflow produzir uma saída incorreta sem gerar um erro, não ficará com dados para analisar. 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. Por isso, o ficheiro em disco não diminui assim que altera a definição.
Para reduzir o pico, em vez do total armazenado, mova menos dados em cada execução. Divida os trabalhos grandes em sub-workflows que devolvam resultados pequenos ao workflow principal, utilize o nó Loop Over Items para processar lotes e mantenha conjuntos de dados completos fora do nó Code.
Os ficheiros binários não devem circular pela memória
N8N_DEFAULT_BINARY_DATA_MODE tem como predefinição default, que mantém os dados binários na memória da execução em curso. Cada ficheiro descarregado por um nó e cada cópia entregue ao nó seguinte permanece aí até à execução terminar. Um workflow que obtenha alguns anexos grandes pode fazer o processo ultrapassar um limite que tarefas normais com JSON nunca atingem. Por isso, a falha ocorre num workflow específico e não num determinado momento.
environment:
- N8N_DEFAULT_BINARY_DATA_MODE=filesystemCom filesystem, os dados binários são gravados em N8N_BINARY_DATA_STORAGE_PATH, que, por predefinição, fica dentro da pasta de utilizador do n8n e, por isso, no mesmo volume que todo o restante conteúdo. Confirme se o volume tem espaço disponível antes de alterar esta opção. N8N_PAYLOAD_SIZE_MAX define o tamanho máximo do payload recebido por webhook, em MiB (mebibytes), e tem o valor predefinido 16. Aumentá-lo permite receber pedidos maiores, mas implica aceitar o custo correspondente na memória.
Tudo o que partilha o servidor compete pela mesma RAM. Se os eventos OOM começaram depois de adicionar um contentor de base de dados, executar a base de dados no Docker ou no host é a decisão que agora tem de tomar.
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 depois de o host reiniciar. restart: unless-stopped reinicia-o nos dois casos, mas respeita um contentor que tenha sido parado manualmente. restart: always também reinicia um contentor que tenha sido parado deliberadamente, assim que o Docker voltar a arrancar.
O n8n disponibiliza um endpoint de health, identificado por N8N_ENDPOINT_HEALTH, que por predefinição é healthz. Teste-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 associado para produzir algum efeito. Escrever um healthcheck que atua efetivamente e fazer o stack arrancar novamente após um reboot abrangem ambas as partes.
O workflow que nunca é executado enquanto o n8n funciona normalmente
Este caso não produz nenhum banner nem reinício. 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 dos casos.
- O workflow não está ativo. Um Schedule Trigger só é executado no caminho de produção, por isso testá-lo no canvas não agenda nada.
- O fuso horário não é o seu.
GENERIC_TIMEZONEusaAmerica/New_Yorkpor predefinição. Assim, 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 devia ser executado enquanto o contentor estava a reiniciar não é executado mais tarde. 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. 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 container antes de editar qualquer ficheiro. - Se o container nunca parou, corrija os cabeçalhos de upgrade do proxy e o tempo limite de inatividade.
- Se
OOMKilledfor verdadeiro, defina deliberadamente um limite para o container, configure o limite máximo do heap do Node abaixo desse valor e altere os dados binários parafilesystem. - Se nada for acionado, confirme se o workflow está ativo e se o fuso horário da instância é o seu.
A maior parte destas definições é configurada uma vez e depois fica esquecida, sobre uma instalação funcional. Se ainda estiver a preparar 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 seu reverse proxy não encaminhar os cabeçalhos Connection: Upgrade e Upgrade: websocket, ou não utilizar HTTP/1.1 no upstream, a atualização nunca é concluída e o browser tenta ligar-se novamente sem parar, enquanto o n8n continua saudável. No nginx, precisa de proxy_http_version 1.1, das duas linhas proxy_set_header e de um proxy_read_timeout superior aos 60 segundos predefinidos, para que um separador sem atividade não seja desligado. Verifique a configuração em execução com sudo nginx -T, não 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 o sinalizador OOMKilled. O valor True significa que o kernel eliminou o processo por exceder um limite de memória. Não haverá informação útil no log do contentor, porque o processo não teve oportunidade de escrever. O valor False, juntamente com um erro de heap e um stack trace no fim de docker logs, significa que o Node.js atingiu o próprio limite de heap do V8 e terminou sozinho. 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, segundo 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. Depois, verifique novamente no dia seguinte, e não imediatamente.
Por que o meu workflow agendado não foi executado enquanto o n8n estava a reiniciar?
O n8n regista os triggers quando o processo inicia. Não repõe os agendamentos que venceram enquanto estava parado. Por isso, um ciclo de reinícios produz silêncio, e não uma sequência de execuções acumuladas. A execução seguinte ocorre na próxima hora prevista depois do arranque. Se precisa de execuções que não possam ser perdidas, acione o workflow através de um chamador externo que faça pedidos a 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 atuar 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.