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 questão colocada ao suporte do n8n.
N8N_HOSTé o nome de host público,n8n.example.com. Se o deixar com o valor predefinidolocalhostatrás de um proxy, o editor tenta carregar a própria API a partir delocalhostno seu navegador, 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 escuta dentro do contentor. Não é a porta pública; o proxy gere a porta 443.WEBHOOK_URL=https://n8n.example.com/é a variável que mais frequentemente causa problemas. O n8n imprime 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 impressos sem qualquer erro, parecem plausíveis, mas não são acessíveis a partir da Internet. Por isso, os pedidos do sistema externo nunca chegam e falham silenciosamente. Defina-a com o URL base público exato, incluindo a barra final. Depois, confirme que o nó de webhook apresenta 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 endereço do proxy. Uma variável que deve deliberadamente não definir aqui é N8N_RUNNERS_ENABLED. Os task runners, que executam a lógica dos nós Code do n8n num processo separado e isolado, são o comportamento predefinido desde a versão 1.69 e são obrigatórios na linha 2.x usada neste guia. Por isso, a opção antiga está obsoleta. Se a definir agora, o n8n apenas regista uma mensagem a indicar que deve removê-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.
Seu primeiro workflow, para colocá-lo em funcionamento
Abra https://n8n.example.com/, crie a conta de proprietário (na próxima seção) e monte o menor workflow que comprova que o caminho funciona: webhook de entrada, chamada HTTP e resposta de saída.
- Adicione um nó Webhook. Defina o método como
POSTe um caminho comohello. Ele mostra 2 URLs, uma Test URL e uma Production URL, que estão na origem de metade dos relatos de que “meu webhook não funciona”. A Test URL responde a 1 chamada e somente enquanto você tiver clicado em Listen for test event; depois, ela expira. A Production URL responde sempre que o workflow está Active. - Adicione um nó HTTP Request depois dele, apontado para qualquer API JSON pública. Uma solicitação GET para
https://api.github.com/zenretorna 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 chamador receba de volta a saída do nó HTTP.
- Ative o workflow (Active, no canto superior direito) e chame-o:
curl -X POST https://n8n.example.com/webhook/hello. Você deverá receber de volta a linha zen: entrada POST, chamada à API e resposta de saída. Essa é a estrutura da maioria das automações reais.
Uma variante agendada substitui o nó Webhook por um Schedule Trigger e chama um endpoint de modelo. Um modelo self-hosted do Ollama executado na mesma VPS é uma forma simples 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 menor 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 significa que um pull não supervisionado pode fornecer uma build que migra a base de dados assim que inicia. Fixe uma versão, leia as notas da versão antes de a atualizar — o n8n assinala aí as alterações incompatíveis — e atualize de forma deliberada:
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 principais são onde 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 o acesso silenciosamente até o alterar novamente para false. A mesma versão começou a impor permissões rigorosas no ficheiro de definições. Leia a página de alterações incompatíveis da versão 2.0 antes de passar para outra versão principal. O n8n executa automaticamente as migrações necessárias da base de dados ao iniciar. É precisamente por isso que o pg_dump antes da atualização não é opcional. Como as credenciais são armazenadas encriptadas com uma chave em .env e os dados ficam no Postgres, os contentores são descartáveis: atualize substituindo-os e reverta fixando a tag anterior e restaurando o dump.
Modos de falha e as mensagens apresentadas
The requested webhook "POST hello" is not registered. Um 404 ao chamar um webhook cujo workflow não está Active, ou ao chamar o caminho de teste quando não há nenhum listener ativo. Os caminhos de teste (/webhook-test/...) respondem apenas enquanto clicou em "Listen for test event"; os caminhos de produção (/webhook/...) respondem apenas quando o toggle do workflow está ativado. O erro semelhante This webhook is not registered for GET requests. Did you mean to make a POST request? significa que o método está incorreto: o node espera POST, mas foi enviado 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 própria API de backend. Quando existe um proxy, isto quase sempre resulta de N8N_HOST ou WEBHOOK_URL incorretos, de o proxy não encaminhar os cabeçalhos Upgrade do WebSocket ou de N8N_PROTOCOL não corresponder à forma como se liga. Confirme as quatro variáveis públicas e verifique se o proxy encaminha Upgrade e Connection.
password authentication failed for user "n8n" nos logs, com o container a reiniciar. A palavra-passe enviada pelo n8n 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 palavra-passe antiga. Reponha a palavra-passe original ou, se não houver dados a preservar, docker compose down e docker volume rm o volume do postgres e inicie-o 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 diretório de configuração. Isto acontece frequentemente quando se faz bind mount de uma pasta do host (./n8n_data:/home/node/.n8n) pertencente a root. Use o named volume mostrado acima ou, se precisar de usar um bind mount, execute sudo chown -R 1000:1000 ./n8n_data primeiro.
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 aplica 0600 a esse ficheiro de definições por predefinição e corrige-o durante o boot. Esta linha de log significa que já corrigiu o modo, normalmente depois de um bind mount ou de um restore que repôs o ficheiro com permissões demasiado permissivas. Não é necessária qualquer 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 linha 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 do ambiente é diferente da chave que o n8n escreveu no volume de dados numa execução anterior. Isto acontece sobretudo quando o n8n gerou uma chave aleatória num boot anterior porque a variável não estava definida e, depois, foi definida uma chave diferente. Reponha 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 recriá-lo, aceitando que as credenciais existentes ficarão ilegíveis.
Um aviso 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 usar diretamente o IP e a porta em vez do 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 predefinição) é suficiente para experimentar o n8n e para uma instância pessoal que execute um workflow de cada vez. Use Postgres para tudo aquilo de que dependa: o bloqueio de escrita único do SQLite gera database is locked com concorrência, e o Postgres permite fazer cópias de segurança corretamente 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 não está definida ou está incorreta, 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. Defina WEBHOOK_URL=https://n8n.example.com/ e confirme que o nó apresenta um URL sem porta. A segunda causa é chamar um webhook cujo workflow não está com o estado Active, o que devolve The requested webhook ... is not registered.
O que tenho de salvaguardar no n8n?
Duas coisas. O N8N_ENCRYPTION_KEY do seu ficheiro .env, porque todas as credenciais armazenadas são cifradas com ele. Se o perder, deixam de poder ser decifradas permanentemente. Copie-o para fora do servidor no dia em que o criar. Faça também um pg_dump da base de dados Postgres que contém os workflows, o histórico e as credenciais. Uma reposição precisa dos dois: da mesma chave e do dump.
Como coloco o n8n atrás de HTTPS?
O n8n disponibiliza HTTP simples na porta 5678. Um reverse proxy à frente termina o TLS. Associe o n8n a 127.0.0.1:5678 para que apenas o proxy possa aceder-lhe. 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 bloqueia.
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, fixe a tag anterior e restaure o dump criado antes da atualização.