Boa parte do conteudo de self-hosting do n8n esta em ingles, e o que existe em portugues normalmente para no docker run. Esse post e o passo a passo completo de uma instalacao que aguenta producao, mais os erros que eu vejo com mais frequencia.
Testado em Ubuntu 22.04 e 24.04.
O que vamos montar
- n8n em Docker, atras do Traefik com HTTPS automatico (Let’s Encrypt)
-
- Postgres como banco (nao SQLite, explico o porque no erro numero 1)
-
- Reinicio automatico, backup, e um caminho de upgrade que nao perde dados
- Requisito minimo realista: 2 GB de RAM e 2 vCPUs. Da para subir com 1 GB, mas o primeiro workflow com um no de IA ou um Code node mais pesado derruba por OOM. Se voce pretende rodar n8n junto com outra coisa no mesmo servidor, 4 GB.
-
Passo 1 - DNS
Antes de tudo, aponte um subdominio para o IP da VPS:
n8n.seudominio.com.br A IP_DA_SUA_VPS
```
Espere propagar. O Let's Encrypt vai falhar se o DNS ainda nao resolver, e o erro que ele devolve nao e obvio.
## Passo 2 - Docker
```bash
curl -fsSL https://get.docker.com | sh
docker compose version
```
## Passo 3 - A stack
```bash
mkdir -p /root/n8n && cd /root/n8n
```
`docker-compose.yml`:
```yaml
services:
traefik:
image: traefik:v3
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
- --certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_HOST: ${N8N_HOST}
N8N_PROTOCOL: https
WEBHOOK_URL: https://${N8N_HOST}/
GENERIC_TIMEZONE: America/Sao_Paulo
TZ: America/Sao_Paulo
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)
- traefik.http.routers.n8n.entrypoints=websecure
- traefik.http.routers.n8n.tls.certresolver=letsencrypt
- traefik.http.services.n8n.loadbalancer.server.port=5678
volumes:
letsencrypt:
postgres_data:
n8n_data:
```
`.env` no mesmo diretorio:
```bash
cat > .env <<EOF
N8N_HOST=n8n.seudominio.com.br
ACME_EMAIL=voce@seudominio.com.br
POSTGRES_PASSWORD=$(openssl rand -hex 24)
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
EOF
chmod 600 .env
```
Sobe:
```bash
docker compose up -d
docker compose logs -f n8n
```
Em um ou dois minutos, `https://n8n.seudominio.com.br` responde com certificado valido.
---
# Os 5 erros
## 1. Deixar o SQLite em producao
O n8n sobe com SQLite por padrao e funciona bem, ate voce ter execucoes concorrentes. Ai comecam os `database is locked`, execucoes que somem, e lentidao crescente conforme a tabela de execucoes engorda.
Migrar depois e chato. Comece com Postgres. E a diferenca de um servico a mais no compose.
## 2. Nao guardar a N8N_ENCRYPTION_KEY
Essa chave criptografa todas as credenciais que voce cadastra no n8n. Se voce perder o volume e nao tiver a chave, todas as credenciais viram lixo ilegivel, mesmo com backup do banco. Voce vai recadastrar tudo, uma a uma.
Guarde a chave num gerenciador de senhas, hoje, antes de continuar lendo.
## 3. Esquecer a WEBHOOK_URL
Sem essa variavel, o n8n gera as URLs de webhook usando o hostname interno do container. O painel mostra uma URL que nao funciona de fora, e voce perde meia hora achando que o problema e no servico que esta chamando.
Se esta atras de proxy, WEBHOOK_URL e obrigatoria.
## 4. Nunca configurar o pruning de execucoes
A tabela execution_entity cresce sem parar. Em uma instancia com workflows rodando de minuto em minuto, sao milhoes de linhas em poucos meses. O banco incha, o disco enche, o n8n fica lento.
Duas variaveis resolvem:
```yaml
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: "336" # horas - 14 dias
```
## 5. Expor a porta 5678
`ports: - "5678:5678"` no servico do n8n coloca o painel na internet sem TLS e sem passar pelo proxy. Um n8n aberto e acesso as suas credenciais e execucao de codigo arbitrario via Code node.
No compose acima o n8n nao publica porta nenhuma. Quem fala com a internet e so o Traefik. Mantenha assim.
Complemente com firewall:
```bash
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
```
---
## Backup
O minimo que salva a sua noite:
```bash
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > n8n-$(date +%F).sql.gz
```
Isso mais a N8N_ENCRYPTION_KEY guardada em outro lugar = recuperacao completa.
## Upgrade
```bash
cd /root/n8n
docker compose pull
docker compose up -d
```
Faca o dump antes. Sempre.
---
Escrevi uma versao mais longa disso em portugues no meu blog, com a parte de monitoramento e as configuracoes de fila para quem precisa de worker separado: https://yousecure.io/blog/n8n-self-hosted-instalando-automacao-vps-ubuntu
Se travar em algum passo, comenta ai que eu respondo.