como instalar n8n no vps com docker e https
Guia para rodar n8n com Docker Compose e Postgres. Aprenda a configurar WEBHOOK_URL e evitar erros de conexão com o reverse proxy e SSL no seu VPS.
O que você está construindo
O n8n é uma ferramenta de automação de workflow: um editor visual onde um trigger — um webhook, um agendamento ou o envio de um formulário — dispara uma cadeia de nodes que chamam APIs, transformam dados e escrevem em outros sistemas. Ele se tornou a solução padrão para workflows de agentes de IA porque se comunica com qualquer provedor de modelo e banco de dados sem que você precise escrever um serviço. Um docker run consegue um editor funcional em dois minutos. Este guia foca nos outros noventa por cento: tornar o sistema durável usando Postgres em vez do arquivo SQLite padrão, torná-lo acessível via HTTPS e — a parte que quase todos erram — configurar webhooks para fornecer uma URL que o mundo externo consiga realmente alcançar.
O stack final consiste em dois containers em uma única rede Docker: o próprio n8n e um banco de dados Postgres que armazena seus workflows e credenciais. Um reverse proxy no host encerra o TLS e encaminha as requisições para o n8n no localhost, garantindo que nada fique exposto à internet exceto através desse proxy. Ele está listado junto com outros serviços na lista de self-hosting de 2026.
Pré-requisitos e limitações reais
Você precisa de uma VPS com pelo menos 1 GB de RAM; planeje usar 2 GB quando os workflows executarem tarefas reais, pois as execuções somadas ao runtime do Node.js consomem muita memória. O OOM killer encerrando o container durante a execução é uma forma ruim de aprender isso. Um único vCPU é suficiente para começar.
Você precisa de um domínio ou subdomínio — por exemplo n8n.example.com — com um registro A apontando para o IP público da VPS que resolva antes de você solicitar um certificado. As portas 80 e 443 devem estar abertas para o proxy; a porta 5678 do n8n não deve estar exposta à internet. Você precisa do Docker Engine e do plugin Compose; se o docker compose version apresentar erro com docker: 'compose' is not a docker command, você possui o binário standalone antigo, e o plugin é sudo apt install docker-compose-plugin.
SQLite serve para testes, Postgres para produção
O banco de dados padrão do n8n é um arquivo SQLite em /home/node/.n8n/database.sqlite. Para testes iniciais ele é suficiente — se você não montar um volume, os dados serão perdidos na primeira recriação do container. O motivo para migrar para Postgres não é apenas velocidade bruta; o SQLite utiliza um lock de escrita único, o que faz com que instâncias executando vários workflows simultâneos, ou o modo queue que você eventualmente precisará, causem SQLITE_BUSY: database is locked sob concorrência. O Postgres não possui esse limite, realiza backups limpos com pg_dump e é o padrão recomendado pela documentação oficial do n8n para servidores de produção. Mudar o banco posteriormente exige migração manual de dados; portanto, se este servidor for importante, comece com Postgres.
DNS e o firewall
Aponte o registro e abra as portas primeiro. Isso evita que a etapa de certificado falhe por 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 arquivo compose vincula o n8n ao 127.0.0.1:5678, portanto apenas o reverse proxy do host pode acessá-lo. Um ufw allow 5678 removeria esse isolamento.
O arquivo Compose
Crie um diretório de trabalho e um docker-compose.yml. Este é o stack completo — 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:Algumas decisões importantes. DB_POSTGRESDB_HOST=postgres é o service name, que o Docker resolve na rede compartilhada — não é o localhost, que dentro do container n8n significa o próprio n8n. O depends_on com condition: service_healthy evita que o n8n tente conectar ao Postgres antes do banco estar pronto; sem isso, o n8n inicia, não encontra o banco de dados e encerra. O volume nomeado n8n_data em /home/node/.n8n armazena a chave de criptografia e, no SQLite, o banco de dados — o único diretório que você não pode perder. Fixe a imagem em uma versão exata, nunca use latest; os motivos estão na seção de upgrade abaixo.
O arquivo de secrets
Nunca coloque senhas no arquivo compose. Coloque-as em um arquivo .env ao lado dele para que o Compose o leia automaticamente, e gere valores que sejam realmente aleatórios.
printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .envO N8N_ENCRYPTION_KEY é a string mais importante aqui — é a chave com a qual cada credencial armazenada é criptografada. Defina-a explicitamente em vez de deixar o n8n gerar uma, pois um valor gerado por você é um valor que você pode anotar e restaurar. Assim que o n8n criptografar sua primeira credencial com esta chave, alterá-la tornará todas as credenciais impossíveis de descriptografar — portanto, defina-a uma vez, agora, e nunca mais altere essa linha.
As variáveis de ambiente que determinam o funcionamento dos webhooks
Quatro variáveis controlam como o n8n se identifica para o mundo externo. Configurações incorretas nestas variáveis são a principal causa de chamados de suporte do n8n.
N8N_HOSTé o hostname público,n8n.example.com. Se mantido no padrãolocalhostatrás de um proxy, o editor tentará carregar sua própria API delocalhostno seu navegador, o que causará erro.N8N_PROTOCOL=httpsinforma ao n8n que ele está operando via TLS, fazendo com que ele marque o cookie de sessãoSecuree gere URLs comhttps://.N8N_PORT=5678é a porta na qual o n8n escuta dentro do container. Não é a porta pública; o proxy utiliza a 443.WEBHOOK_URL=https://n8n.example.com/é a variável crítica. O n8n gera os endereços de webhook que você cola no Stripe, GitHub ou qualquer serviço externo combinando estes valores. Se estiver vazia ou incorreta, o n8n usaráN8N_HOST:N8N_PORTe forneceráhttps://n8n.example.com:5678/webhook/...ou, pior,http://localhost:5678/webhook/...— exibidos sem erro, com aparência válida, mas inacessíveis pela internet. Isso faz com que as requisições do chamador nunca cheguem. Configure-a com a URL base pública exata e com a barra final; depois, confirme se o nó de webhook exibe uma URL sem porta.
N8N_PROXY_HOPS=1 instrui o servidor Express do n8n a confiar em um proxy à frente dele. Isso permite que o rate-limiting e recursos que leem o IP do cliente vejam o endereço real em vez do endereço do proxy. Uma variável que você deliberadamente não deve configurar aqui é N8N_RUNNERS_ENABLED: os task runners — que executam a lógica do Code-node em um processo sandboxed separado — são o padrão desde a versão 1.69 e são obrigatórios a partir da linha 2.x (versão utilizada neste guia); portanto, a opção antiga foi descontinuada. Se configurá-la agora, o n8n apenas registrará um aviso solicitando a remoção.
Primeiro boot
docker compose up -d
docker compose ps
docker compose logs -f n8nUm primeiro boot bem-sucedido termina com uma linha Editor is now accessible via:, com uma linha n8n ready on ..., port 5678 logo acima. O docker compose ps deve mostrar ambos os containers Up, com o postgres marcado como (healthy). Se o n8n entrar em um loop de Restarting, verifique os logs — o problema quase sempre é a conexão com o banco de dados ou as permissões de volume descritas abaixo.
TLS com um reverse proxy
O n8n utiliza HTTP puro na porta 5678; um serviço externo deve encerrar o HTTPS. Existem duas opções viáveis.
Se você já executa vários containers, coloque o n8n atrás de um reverse proxy Traefik que emite certificados TLS automaticamente usando algumas labels — o Traefik solicita e renova o certificado para você.
Se este for o único app no servidor, um virtual host do nginx com um certificado Let's Encrypt é mais simples. Use a configuração de TLS do Certbot e nginx para Ubuntu 24.04 para obter o certificado e, em seguida, use este server block:
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 headers Upgrade e Connection "upgrade" não são opcionais. O n8n envia atualizações de execução em tempo real para o editor via WebSocket; sem essas duas linhas, a página de login carrega e depois trava com um banner de conexão perdida. O proxy_read_timeout 3600 evita que execuções longas sejam interrompidas pelo timeout padrão de 60 segundos do nginx. O header X-Forwarded-Proto $scheme é o complemento do N8N_PROXY_HOPS=1: ele informa ao n8n que a requisição original foi via HTTPS, mesmo que o proxy a alcance via HTTP puro, evitando que o n8n considere a conexão insegura e rejeite seu próprio cookie.
Seu primeiro workflow, para torná-lo real
Abra o https://n8n.example.com/, crie a conta de proprietário (próxima seção) e construa o menor workflow que comprove que o caminho funciona: um webhook de entrada, uma chamada HTTP e uma resposta de saída.
- Adicione um nó Webhook. Defina o método como
POSTe um path comohello. Ele exibe duas URLs, uma Test URL e uma Production URL — a causa de metade dos relatos de "meu webhook não funciona". A Test URL responde a apenas uma chamada e apenas enquanto você clicar em Listen for test event; ela expira em seguida. A Production URL responde sempre que o workflow estiver Active. - Adicione um nó HTTP Request logo após ele, apontando para qualquer API JSON pública — um GET para
https://api.github.com/zenretorna uma string de uma linha, o que é suficiente. - Adicione um nó Respond to Webhook e configure a opção Respond do nó Webhook para "Using Respond to Webhook node", para que o chamador receba o output do nó HTTP de volta.
- Ative o workflow (Active no canto superior direito) e faça a chamada:
curl -X POST https://n8n.example.com/webhook/hello. Você deve receber a linha de texto de volta — POST de entrada, chamada de API, resposta de saída, o padrão da maioria das automações reais.
Uma variante agendada substitui o nó Webhook por um Schedule Trigger e chama um endpoint de modelo em vez disso — um endpoint self-hosted de um Ollama rodando no mesmo VPS é uma forma prática de construir um sumarizador noturno.
Gerenciamento de usuários, não auth básica
Guias antigos do n8n recomendam configurar N8N_BASIC_AUTH_ACTIVE=true. Essas variáveis foram removidas no n8n 1.0 e não têm efeito agora. A autenticação atual é a owner account: ao carregar o editor pela primeira vez, o n8n exige a criação de um usuário proprietário com e-mail e senha. Esse acesso é obrigatório — não existe modo anônimo. Crie a conta imediatamente após o primeiro boot, antes de fornecer a URL a qualquer pessoa: entre docker compose up e o envio desse primeiro formulário, a instância pode ser reivindicada por quem a acessar primeiro. Uma camada de basic-auth via reverse-proxy é uma trava extra razoável, mas funciona como um segundo fator, não como a autenticação real.
Backups: primeiro a chave de criptografia, depois o banco de dados
Dois itens precisam de backup, e eles não são igualmente substituíveis.
O N8N_ENCRYPTION_KEY. Todas as credenciais armazenadas no n8n — tokens de API, senhas de banco de dados, segredos OAuth — são criptografadas em repouso com esta chave. Os workflows no Postgres tornam-se inúteis sem ela: se você restaurar o banco de dados em um novo servidor com uma chave diferente, o n8n não conseguirá descriptografar nenhuma credencial, sem possibilidade de recuperação ou reset. O seu arquivo .env contém a chave; copie-o para fora do servidor — um gerenciador de senhas é o ideal — no dia em que criá-lo. Este é o backup que realmente importa.
O banco de dados Postgres, para os workflows, histórico de execução e as próprias credenciais criptografadas:
docker compose exec -T postgres pg_dump -U n8n -d n8n \
| gzip > n8n-db-$(date +%F).sql.gzExecute este comando periodicamente e copie o dump para fora do servidor. Para restaurar em um novo VPS: inicie o stack uma vez para que o banco de dados exista, pare o n8n, carregue o dump com psql, insira a mesma N8N_ENCRYPTION_KEY no .env e inicie o n8n. A chave original somada ao dump resulta em uma instância funcional; uma chave nova resulta em workflows que não conseguem usar nenhuma credencial.
Upgrades: fixe a tag
O arquivo compose fixa a versão n8nio/n8n:2.29.10 em vez de latest propositalmente. O n8n lança uma nova versão minor semanalmente e ocasionalmente altera o schema do banco de dados ou o comportamento dos nodes entre elas. Por isso, latest significa que um pull automático pode entregar uma build que migra seu banco de dados assim que inicia. Fixe uma versão, leia as release notes antes de atualizar — o n8n lista breaking changes lá — e faça o upgrade 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 n8nSaltos de major-version são onde isso é mais crítico. A linha 2.0, por exemplo, alterou N8N_BLOCK_ENV_ACCESS_IN_NODE para true por padrão; qualquer Code node que utilizasse process.env perderá o acesso silenciosamente até que você o defina novamente como false. A mesma release passou a exigir permissões estritas no arquivo de settings. Leia a página de breaking-changes da 2.0 antes de mudar de uma major-version para outra. O n8n executa qualquer migração de banco de dados necessária automaticamente no início — é exatamente por isso que o pg_dump pré-upgrade não é opcional. Como as credenciais são armazenadas criptografadas com uma chave em .env e os dados residem no Postgres, os containers são descartáveis: você faz o upgrade substituindo-os e faz o rollback fixando a tag anterior e restaurando o dump.
Modos de falha e as strings que você verá
The requested webhook "POST hello" is not registered. Um erro 404 ao chamar um webhook cujo workflow não está Active, ou ao chamar o path de teste quando não há ninguém escutando. Paths de teste (/webhook-test/...) respondem apenas enquanto você clicou em "Listen for test event"; paths de produção (/webhook/...) respondem apenas quando o toggle do workflow está ligado. O erro de método 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 e você enviou GET.
A URL do webhook mostra :5678 ou localhost. O node exibe https://n8n.example.com:5678/webhook/... ou http://localhost:5678/.... WEBHOOK_URL está indefinido ou incorreto, então o n8n construiu o endereço a partir de N8N_HOST:N8N_PORT em vez da sua base pública. Configure WEBHOOK_URL=https://n8n.example.com/, recrie o container com docker compose up -d, e a porta desaparecerá.
There was a problem loading init data no browser. O editor carregou, mas não consegue alcançar sua própria API de backend. Atrás de um proxy, isso quase sempre é um N8N_HOST ou WEBHOOK_URL incorreto, um proxy sem os headers de WebSocket Upgrade, ou N8N_PROTOCOL não condizente com a sua conexão. Confirme as quatro variáveis públicas e se o proxy encaminha Upgrade e Connection.
password authentication failed for user "n8n" nos logs, com o container reiniciando. A senha enviada pelo n8n não coincide com a senha usada na inicialização do banco de dados. O problema: o Postgres lê POSTGRES_PASSWORD apenas quando inicializa um diretório de dados vazio. Inicie a stack uma vez, altere POSTGRES_PASSWORD em .env, e o volume postgres_data existente ainda conterá a senha antiga. Volte para a original ou, se não houver dados para manter, execute docker compose down e docker volume rm no volume do postgres e inicie-o do zero.
EACCES: permission denied, open '/home/node/.n8n/config' na inicialização. O n8n roda como o usuário node (UID 1000) e não consegue escrever no diretório de configuração. Isso ocorre quando usuários fazem bind-mount de uma pasta do host (./n8n_data:/home/node/.n8n) pertencente ao root. Use o volume nomeado mostrado acima ou, se insistir no 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 impõe 0600 no arquivo de configurações por padrão e o corrige automaticamente no boot — esta linha de log significa que ele já corrigiu o modo, comumente após um bind mount ou após um restore copiar o arquivo de volta com permissões permissivas. Nenhuma ação é necessária; configure N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false apenas se o seu filesystem realmente não puder suportar permissões.
Mismatching encryption keys — a linha completa diz que a chave de criptografia no arquivo de configurações /home/node/.n8n/config não coincide com a N8N_ENCRYPTION_KEY no seu ambiente. A chave no seu ambiente é diferente da que o n8n escreveu no volume de dados em uma execução anterior — geralmente porque o n8n gerou uma chave aleatória em um boot anterior quando a variável estava vazia, e você definiu uma diferente depois. Coloque a chave original de volta em .env ou, apenas se você realmente não tiver credenciais salvas que valham a pena manter, delete o arquivo config dentro do volume n8n_data e deixe o n8n regenerá-lo — ciente de que as credenciais existentes ficarão ilegíveis.
Um banner 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. Você configurou N8N_PROTOCOL=https mas acessou o n8n via HTTP simples — geralmente acessando o IP e a porta diretamente em vez do proxy HTTPS. Acesse via https://n8n.example.com/. Apenas se você realmente não puder usar HTTPS você deve configurar N8N_SECURE_COOKIE=false, e nunca em uma máquina exposta à internet.
Para colocar um modelo de linguagem dentro desses workflows, veja building AI workflows with Claude and n8n.
FAQ
Devo usar SQLite ou Postgres para o n8n?
O SQLite (padrão) é adequado para testes ou instâncias pessoais que executam um workflow por vez. Mude para Postgres para qualquer ambiente de produção: o lock de escrita única do SQLite causa database is locked sob concorrência, e o Postgres realiza backups limpos com pg_dump. A migração posterior é manual, portanto, se o servidor for crítico, comece com Postgres.
Por que meus webhooks do n8n nunca disparam?
Quase sempre é WEBHOOK_URL. Se estiver indefinido ou incorreto, o n8n gera endereços de webhook baseados em N8N_HOST:N8N_PORT — frequentemente com :5678 ou localhost — que parecem válidos, mas são inacessíveis pela internet, impedindo a chegada das requisições. Configure WEBHOOK_URL=https://n8n.example.com/ e confirme se o node exibe uma URL sem porta. A segunda causa é chamar um webhook de um workflow que não está em modo Active, o que retorna The requested webhook ... is not registered..
O que eu devo fazer backup no n8n?
Duas coisas. O N8N_ENCRYPTION_KEY do seu arquivo .env, pois cada credencial armazenada é criptografada com ele e perdê-lo torna as credenciais permanentemente indecifráveis — copie-o para fora do servidor no dia da criação. E um pg_dump do banco de dados Postgres para workflows, histórico e credenciais. O restore exige ambos: a mesma chave e o dump.
Como coloco o n8n atrás de um HTTPS?
O n8n serve HTTP puro na porta 5678; um reverse proxy à frente encerra o TLS. Vincule o n8n ao 127.0.0.1:5678 para que apenas o proxy tenha acesso, então use Traefik com certificados automáticos ou nginx com um certificado Let's Encrypt. Configure N8N_PROTOCOL=https e WEBHOOK_URL=https://your-host/, e certifique-se de que o proxy encaminhe os headers de WebSocket Upgrade, caso contrário o editor travará.
Como atualizar o n8n com segurança?
Fixe uma tag de imagem específica em vez de latest, faça um pg_dump primeiro porque o n8n executa migrações automaticamente ao iniciar, leia as notas de lançamento para mudanças que quebram a compatibilidade, então atualize a tag e execute docker compose pull n8n && docker compose up -d n8n. O container é descartável, então para fazer rollback, fixe a tag anterior e restaure o dump feito antes da atualização.