Como hospedar n8n em VPS com Docker e HTTPS
Instale n8n em VPS com Docker Compose, Postgres e HTTPS via reverse proxy. Evite os erros de WEBHOOK_URL e encryption key que quebram webhooks e credenciais.
O que você vai criar
n8n é uma ferramenta de automação de workflows: um editor visual em que um trigger, um webhook, uma agenda ou o envio de um formulário inicia uma cadeia de nodes que chama APIs, transforma dados e grava informações noutros sistemas. Tornou-se a ferramenta de integração padrão para workflows de agentes de IA porque comunica com todos os fornecedores de modelos e bases de dados sem ser necessário escrever um serviço. Um docker run permite ter um editor funcional em dois minutos. Este guia aborda os outros noventa por cento: tornar a instalação persistente com Postgres em vez do ficheiro SQLite predefinido, disponibilizá-la através de HTTPS e, na parte que quase toda a gente configura incorretamente, fazer com que os webhooks forneçam um URL que o mundo exterior consiga efetivamente alcançar.
A stack final tem dois containers na mesma rede Docker: o próprio n8n e uma base de dados Postgres que armazena os workflows e as credenciais. Um reverse proxy no host termina o TLS e encaminha as ligações para o n8n em localhost, pelo que nada fica exposto à Internet sem passar por esse proxy. A instalação fica juntamente com os outros serviços da lista de self-hosting de 2026.
Pré-requisitos e limites reais
É necessário um VPS com pelo menos 1 GB de RAM. Planeje 2 GB quando os workflows começarem a executar tarefas reais, porque as execuções e o runtime do Node.js consomem memória. O OOM killer pode interromper o container durante a execução. Essa é uma forma ruim de descobrir a limitação.
Uma vCPU é suficiente para começar. Se este servidor também executar algo mais pesado, dimensione-o primeiro para esse serviço. Uma biblioteca de fotos é o caso mais comum. Os requisitos reais de RAM do PhotoPrism e do Immich são muito superiores aos do n8n.
O mesmo se aplica a um servidor de mídia. Um servidor Jellyfin e uma interface web para navegar pelo conteúdo, como o Halcyon, que reconstrói a biblioteca como uma locadora dos anos 90, consumirão a RAM e a capacidade disponível para transcodificação muito antes de o n8n perceber.
Precisa de um domínio ou subdomínio, por exemplo n8n.example.com, com um registo A a apontar para o IP público do VPS. Esse registo deve resolver antes de solicitar um certificado. As portas 80 e 443 têm de estar abertas para o proxy. A porta 5678 do n8n não pode ficar exposta à Internet. Precisa do Docker Engine e do plugin Compose. Se docker compose version devolver um erro com docker: 'compose' is not a docker command, tem o binário autónomo antigo, e o plugin é sudo apt install docker-compose-plugin.
SQLite é suficiente para testes; use Postgres para qualquer instalação de que dependa
A base de dados predefinida do n8n é um ficheiro SQLite em /home/node/.n8n/database.sqlite. Para testar rapidamente, é suficiente: não monte nenhum volume e perderá os dados na primeira recriação do contentor, o que também serve como uma lição. A razão para mudar para Postgres não é a velocidade bruta. É o facto de o SQLite manter um único bloqueio de escrita, pelo que uma instância que execute vários workflows em simultâneo, ou o modo de fila que provavelmente irá querer mais tarde, gera SQLITE_BUSY: database is locked em condições de concorrência. O Postgres não tem esse limite, permite criar cópias de segurança corretamente com pg_dump e é a base de dados que a documentação do n8n pressupõe para um servidor de que dependa. Mudar mais tarde implica migrar os dados manualmente. Se este servidor for importante, comece com Postgres.
DNS e o firewall
Aponte o registo e abra primeiro as portas, para que a etapa do certificado não falhe mais tarde devido a um nome que não resolve.
dig +short n8n.example.com
curl -s ifconfig.me
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw enableNão abra a porta 5678. O ficheiro compose associa o n8n a 127.0.0.1:5678, para que apenas o reverse proxy do host possa aceder a ele. Um ufw allow 5678 desfaria esse isolamento.
O ficheiro Compose
Crie um diretório de trabalho e um docker-compose.yml. Esta é a stack completa: dois serviços, uma rede privada e dois volumes nomeados.
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:2.29.10
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.example.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.example.com/
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_PROXY_HOPS=1
- GENERIC_TIMEZONE=Europe/London
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
networks:
- n8n_net
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
n8n_data:
networks:
n8n_net:Há algumas decisões que vale a pena indicar explicitamente. DB_POSTGRESDB_HOST=postgres é o nome do serviço, que o Docker resolve na rede partilhada, e não localhost, que dentro do contentor n8n significa o próprio n8n. O depends_on com condition: service_healthy impede que o n8n entre em competição com o Postgres durante o arranque; sem isto, o n8n inicia, não encontra a base de dados e termina. O volume nomeado n8n_data em /home/node/.n8n contém a chave de encriptação e, com SQLite, a base de dados: é o único diretório que não pode perder. Fixe a imagem numa versão exata, nunca latest; os motivos são explicados na secção de atualização abaixo.
O ficheiro de segredos
Nunca coloque palavras-passe no ficheiro Compose. Coloque-as num ficheiro .env junto dele, que o Compose lê automaticamente, e gere-as para que sejam realmente aleatórias.
printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .envA N8N_ENCRYPTION_KEY é a cadeia mais importante deste ficheiro. É a chave usada para encriptar todas as credenciais armazenadas. Defina-a explicitamente em vez de deixar que o n8n a gere, porque um valor gerado por si pode ser registado e restaurado. Depois de o n8n encriptar a primeira credencial com esta chave, alterá-la torna todas as credenciais impossíveis de desencriptar. Por isso, defina-a agora, uma única vez, e nunca volte a alterar essa linha.
As variáveis de ambiente que determinam se os webhooks funcionam
Quatro variáveis controlam a forma como o n8n se apresenta ao exterior. Configurá-las incorretamente é a principal causa de pedidos de suporte sobre o n8n.
N8N_HOSTé o nome de host público,n8n.example.com. Se o deixar no valor predefinidolocalhostatrás de um proxy, o editor tenta carregar a própria API a partir delocalhostno seu browser, o que falha.N8N_PROTOCOL=httpsinforma o n8n de que o serviço é disponibilizado através de TLS. Assim, o n8n marca o cookie de sessão comoSecuree cria URLshttps://.N8N_PORT=5678é a porta em que o n8n fica à escuta dentro do container. Não é a porta pública; o proxy gere a porta 443.WEBHOOK_URL=https://n8n.example.com/é a variável que costuma causar problemas. O n8n apresenta os endereços dos webhooks que cola no Stripe, no GitHub ou em qualquer sistema externo, construindo-os a partir destes valores. Se estiver ausente ou incorreta, o n8n recorre aN8N_HOST:N8N_PORTe fornecehttps://n8n.example.com:5678/webhook/...ou, pior ainda,http://localhost:5678/webhook/.... Esses endereços são apresentados sem erro, parecem plausíveis, mas não são acessíveis a partir da Internet. Por isso, os pedidos do sistema externo nunca chegam ao n8n. Defina-a como o URL base público exato, incluindo a barra final. Depois, confirme que o nó de webhook mostra um URL sem porta.
N8N_PROXY_HOPS=1 informa o servidor Express do n8n de que existe um proxy à sua frente. Assim, a limitação de taxa e qualquer funcionalidade que leia o endereço IP do cliente veem o endereço real, e não o do proxy. Uma variável que deliberadamente não deve definir aqui é N8N_RUNNERS_ENABLED. Os task runners, que executam a lógica dos nós Code do n8n num processo separado do processo principal, eram opcionais em toda a linha 1.x. Nessa linha, a variável tinha o valor predefinido false e era necessário defini-la como true. O n8n 2.0 ativou-os por predefinição. Por isso, na linha 2.x, este guia fixa a variável como obsoleta, e a documentação do n8n indica que já não é necessário defini-la.
Primeiro arranque
docker compose up -d
docker compose ps
docker compose logs -f n8nUm primeiro arranque normal termina com uma linha Editor is now accessible via:, com uma linha n8n ready on ..., port 5678 acima. docker compose ps deve mostrar ambos os contentores Up, com postgres marcado como (healthy). Se o n8n ficar num ciclo de Restarting, consulte os logs. Quase sempre, a causa é a ligação à base de dados ou as permissões do volume descritas abaixo.
TLS com um reverse proxy
O próprio n8n utiliza HTTP simples na porta 5678; algum componente à frente termina o TLS. Existem duas opções simples.
Se já executa vários contentores, coloque o n8n atrás de um reverse proxy Traefik que emite certificados TLS automaticamente com alguns labels. O Traefik solicita e renova o certificado por si.
Se esta for a única aplicação no servidor, um virtual host nginx com um certificado Let's Encrypt é mais simples. Utilize a configuração de TLS do Certbot e do nginx para Ubuntu 24.04 para obter o certificado e, em seguida, use este bloco de servidor:
server {
listen 443 ssl;
server_name n8n.example.com;
ssl_certificate /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600;
client_max_body_size 16m;
}
}Os cabeçalhos Upgrade e Connection "upgrade" são obrigatórios. O n8n envia atualizações de execução em tempo real para o editor através de um WebSocket. Sem estas duas linhas, a página de início de sessão é carregada e fica bloqueada, apresentando um aviso de perda de ligação. proxy_read_timeout 3600 impede que as execuções longas sejam interrompidas ao fim dos 60 segundos predefinidos do nginx. O cabeçalho X-Forwarded-Proto $scheme complementa N8N_PROXY_HOPS=1: informa o n8n de que o pedido original utilizou HTTPS, embora o proxy o alcance através de HTTP simples. Assim, o n8n não considera a ligação insegura nem rejeita o seu próprio cookie.
O seu primeiro workflow, para o tornar real
Abra https://n8n.example.com/, crie a conta do proprietário (na secção seguinte) e crie o workflow mais pequeno que prove que o percurso funciona: entrada por webhook, chamada HTTP e resposta.
- Adicione um nó Webhook. Defina o método como
POSTe um caminho comohello. São apresentadas duas URLs, uma Test URL e uma Production URL, que estão na origem de metade dos relatos de que “o meu webhook não funciona”. A Test URL responde a uma chamada, e apenas enquanto tiver clicado em Listen for test event; depois expira. A Production URL é registada quando publicar o workflow e continua a responder a partir desse momento. - Adicione um nó HTTP Request depois dele, apontado para qualquer API JSON pública. Um GET para
https://api.github.com/zendevolve uma string de uma linha, o que é suficiente. - Adicione um nó Respond to Webhook e defina a opção Respond do nó Webhook como "Using Respond to Webhook node", para que o autor da chamada receba de volta o resultado do nó HTTP.
- Clique em Publish (no canto superior direito) e faça a chamada:
curl -X POST https://n8n.example.com/webhook/hello. Deverá receber de volta a linha zen: entrada por POST, chamada à API e resposta, que é a estrutura da maioria das automações reais. Os guias mais antigos indicam ativar aqui um botão Active; o n8n 2.0 substituiu esse botão por Publish e Unpublish.
Uma variante agendada substitui o nó Webhook por um Schedule Trigger e chama um endpoint de modelo. Uma instância self-hosted do Ollama em execução no mesmo VPS é uma forma organizada de criar um sumarizador noturno.
Gestão de utilizadores, não autenticação básica
Guias antigos do n8n indicam definir N8N_BASIC_AUTH_ACTIVE=true. Essas variáveis foram removidas no n8n 1.0 e já não têm efeito. Atualmente, a autenticação é feita pela conta de proprietário: na primeira vez que abrir o editor, o n8n solicita a criação de uma conta de proprietário com email e palavra-passe. Essa proteção é obrigatória; não existe um modo anónimo. Crie a conta imediatamente depois do primeiro arranque, antes de partilhar o URL: entre docker compose up e o primeiro envio desse formulário, qualquer pessoa que consiga aceder à instância pode reclamá-la primeiro. Uma camada adicional de autenticação básica no reverse proxy é uma proteção complementar razoável, mas é um segundo fator, não a autenticação principal. A conta de proprietário e todo o restante conteúdo deste guia funcionam na edição comunitária gratuita. Se mais tarde precisar de utilizadores adicionais com funções granulares ou de SSO, vale a pena ler quais funcionalidades do n8n exigem uma licença paga antes de fazer planos com base nelas.
Backups: a chave de encriptação primeiro, depois a base de dados
É necessário fazer backup de duas coisas, e elas não podem ser substituídas da mesma forma.
A N8N_ENCRYPTION_KEY. Todas as credenciais armazenadas no n8n, tokens de API, palavras-passe da base de dados e segredos OAuth são encriptados em repouso com esta chave. Os workflows no Postgres não servem para nada sem ela: se restaurar a base de dados num servidor novo com uma chave diferente, o n8n não consegue desencriptar uma única credencial, sem recuperação nem reposição. O ficheiro .env contém a chave. Copie-o para fora do servidor no dia em que o criar. Guardá-lo numa entrada de um gestor de palavras-passe é o ideal. Este é o backup que realmente importa.
A base de dados Postgres, que contém os workflows, o histórico de execuções e as próprias credenciais encriptadas:
docker compose exec -T postgres pg_dump -U n8n -d n8n \
| gzip > n8n-db-$(date +%F).sql.gzExecute isto segundo um agendamento e copie o dump para fora do servidor. Para restaurar num VPS novo: inicie a stack uma vez para que a base de dados seja criada, pare o n8n, carregue novamente o dump com psql, coloque o mesmo N8N_ENCRYPTION_KEY em .env e inicie o n8n. A mesma chave mais o dump produzem uma instância funcional; uma chave nova deixa workflows que não conseguem usar uma única credencial.
Atualizações: fixe a tag
O ficheiro Compose fixa n8nio/n8n:2.29.10 em vez de latest de propósito. O n8n lança uma nova versão minoritária na maioria das semanas e, ocasionalmente, altera o esquema da base de dados ou o comportamento dos nós entre versões. Por isso, latest pode fazer com que uma atualização automática descarregue uma compilação que migra a base de dados assim que arranca. Fixe uma versão, leia as notas da versão antes de a atualizar — o n8n assinala aí as alterações incompatíveis — e faça a atualização de forma controlada:
docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > pre-upgrade.sql.gz
# edit the image tag in docker-compose.yml, then:
docker compose pull n8n
docker compose up -d n8n
docker compose logs -f n8nAs atualizações entre versões majoritárias são as situações em que isto é mais importante. A linha 2.0, por exemplo, alterou N8N_BLOCK_ENV_ACCESS_IN_NODE para true por predefinição. Assim, qualquer nó Code que lesse process.env perde silenciosamente o acesso até o repor para false. A mesma versão começou a aplicar permissões estritas ao ficheiro de definições, ativou os task runners por predefinição e substituiu o botão de ativação do workflow por Publish. Leia a página sobre alterações incompatíveis da versão 2.0 antes de atravessar uma fronteira de versão majoritária. O n8n executa automaticamente as migrações de base de dados necessárias no arranque. É precisamente por isso que o pg_dump antes da atualização é obrigatório. Como as credenciais são armazenadas cifradas com uma chave em .env e os dados ficam no Postgres, os containers podem ser eliminados e recriados. Para atualizar, substitua-os. Para reverter, fixe a tag anterior e restaure o dump.
Modos de falha e as mensagens que verá
The requested webhook "POST hello" is not registered. Um 404 ao chamar um webhook cujo workflow não está publicado ou ao chamar o caminho de teste quando não há nenhum serviço a escutar. Os caminhos de teste (/webhook-test/...) respondem apenas enquanto clicou em "Listen for test event"; os caminhos de produção (/webhook/...) respondem apenas depois de o workflow ser publicado. O marcador irmão This webhook is not registered for GET requests. Did you mean to make a POST request? significa que o método está errado: o node espera POST, mas enviou GET.
O URL do webhook mostra :5678 ou localhost. O node apresenta https://n8n.example.com:5678/webhook/... ou http://localhost:5678/.... WEBHOOK_URL não está definido ou está incorreto, por isso o n8n criou o endereço a partir de N8N_HOST:N8N_PORT em vez da sua base pública. Defina WEBHOOK_URL=https://n8n.example.com/, recrie o container com docker compose up -d e a porta deixa de aparecer.
There was a problem loading init data no browser. O editor foi carregado, mas não consegue alcançar a sua própria API de backend. Por trás de um proxy, isto quase sempre resulta de N8N_HOST ou WEBHOOK_URL incorreto, de um proxy sem os cabeçalhos Upgrade do WebSocket ou de N8N_PROTOCOL não corresponder à forma como se liga. Confirme as quatro variáveis expostas publicamente e que o proxy encaminha Upgrade e Connection.
password authentication failed for user "n8n" nos logs, com o container a reiniciar. A password que o n8n envia não corresponde àquela com que a base de dados foi inicializada. O ponto importante é que o Postgres lê POSTGRES_PASSWORD apenas quando inicializa um diretório de dados vazio. Inicie a stack uma vez, altere depois POSTGRES_PASSWORD em .env e o volume postgres_data existente continuará a conter a password antiga. Volte a definir a password original ou, se não tiver dados a preservar, docker compose down e docker volume rm o volume do postgres e inicie tudo novamente.
EACCES: permission denied, open '/home/node/.n8n/config' no arranque. O n8n é executado como o utilizador node (UID 1000) e não consegue escrever no seu diretório de configuração. Isto acontece frequentemente quando se monta uma pasta do host (./n8n_data:/home/node/.n8n) pertencente a root. Use o volume nomeado mostrado acima ou, se insistir em usar um bind mount, execute primeiro sudo chown -R 1000:1000 ./n8n_data.
Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. A partir da linha 2.x, o n8n impõe 0600 nesse ficheiro de definições por predefinição e corrige-o automaticamente no arranque. Esta mensagem de log significa que já corrigiu o modo, normalmente depois de um bind mount ou de um restore que voltou a copiar o ficheiro com permissões demasiado permissivas. Não é necessária nenhuma ação; defina N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false apenas se o seu sistema de ficheiros não conseguir realmente suportar permissões.
Mismatching encryption keys; a mensagem completa indica que a chave de encriptação no ficheiro de definições /home/node/.n8n/config não corresponde a N8N_ENCRYPTION_KEY no seu ambiente. A chave no ambiente é diferente daquela que o n8n escreveu no volume de dados durante uma execução anterior. Isto acontece sobretudo quando o n8n gerou uma chave aleatória num arranque anterior, porque a variável não estava definida, e depois definiu uma chave diferente. Volte a colocar a chave original em .env ou, apenas se não tiver credenciais armazenadas que queira preservar, elimine o ficheiro config dentro do volume n8n_data e deixe o n8n regenerá-lo, aceitando que as credenciais existentes ficarão ilegíveis.
Uma mensagem de login sobre cookies seguros: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. Definiu N8N_PROTOCOL=https, mas acedeu ao n8n através de HTTP simples, normalmente ao ligar-se diretamente ao IP e à porta em vez de usar o proxy HTTPS. Aceda através de https://n8n.example.com/. Só deve definir N8N_SECURE_COOKIE=false se não puder realmente usar HTTPS e nunca num servidor exposto à Internet.
Para colocar um modelo de linguagem nesses workflows, consulte como criar workflows de IA com Claude e n8n.
FAQ
Devo usar SQLite ou Postgres no n8n?
SQLite (a opção predefinida) é suficiente para experimentar o n8n e para uma instância pessoal que execute um workflow de cada vez. Use Postgres para tudo o que for importante: o bloqueio de escrita único do SQLite gera database is locked com concorrência, e o Postgres permite fazer cópias de segurança de forma limpa com pg_dump. A migração posterior é manual. Se o servidor for importante, comece com Postgres.
Porque é que os meus webhooks do n8n nunca são acionados?
Quase sempre por causa de WEBHOOK_URL. Quando essa variável não está definida ou tem um valor incorreto, o n8n apresenta endereços de webhook construídos a partir de N8N_HOST:N8N_PORT, muitas vezes com um :5678 ou localhost, que parecem válidos mas não são acessíveis a partir da Internet. Por isso, os pedidos do cliente nunca chegam ao servidor. Defina WEBHOOK_URL=https://n8n.example.com/ e confirme que o nó apresenta um URL sem porta. A segunda causa é chamar o URL de produção de um workflow que ainda não publicou. Nesse caso, é devolvido The requested webhook ... is not registered.
O que tenho de incluir nas cópias de segurança do n8n?
Duas coisas. O N8N_ENCRYPTION_KEY do seu ficheiro .env, porque cada credencial armazenada é encriptada com essa chave. Se a perder, as credenciais deixam de poder ser desencriptadas permanentemente. Copie-a para fora do servidor no dia em que a criar. E um pg_dump da base de dados Postgres, que contém os workflows, o histórico e as credenciais. Uma restauração precisa dos dois elementos: a mesma chave e o dump.
Como coloco o n8n atrás de HTTPS?
O n8n disponibiliza HTTP simples na porta 5678. Um reverse proxy à frente do n8n termina o TLS. Vincule o n8n a 127.0.0.1:5678 para que apenas o proxy lhe possa aceder. Depois, use Traefik com certificados automáticos ou nginx com um certificado Let's Encrypt. Defina N8N_PROTOCOL=https e WEBHOOK_URL=https://your-host/. Confirme também que o proxy encaminha os cabeçalhos Upgrade do WebSocket. Caso contrário, o editor deixa de responder.
Como atualizo o n8n com segurança?
Fixe uma tag de imagem específica em vez de latest. Faça primeiro um pg_dump, porque o n8n executa migrações automaticamente no arranque. Leia as notas da versão para identificar alterações incompatíveis. Depois, atualize a tag e execute docker compose pull n8n && docker compose up -d n8n. O contentor é descartável. Para reverter a atualização, fixe a tag anterior e restaure o dump criado antes da atualização.