Resumo
Neste guia, vamos preparar uma VM Ubuntu 24.04 Minimal para rodar via Docker as aplicações:
- n8n
- Evolution API
- PostgreSQL
- Redis
- Nginx (reverse proxy)
- Certbot (TLS/HTTPS automático)
Usaremos uma VM simples (1 vCPU, 1 GB RAM) e mostrarei como otimizar recursos e isolar os serviços de forma segura.
Pré-requisitos
- Ubuntu 24.04 Minimal
- Acesso SSH
- DNS apontado para o IP público da VM:
- n8n.seu-dominio.com -> IP da VM
- api.seu-dominio.com -> IP da VM
Visão geral da arquitetura
- n8n e Evolution API rodam em containers e compartilham os serviços internos:
- PostgreSQL: bancos distintos (ex.: n8n_db e evolution_db)
- Redis: databases lógicos distintos (ex.: 0 para n8n, 1 para a API)
- Nginx faz o proxy reverso e serve os desafios do Certbot (HTTP-01) para emitir certificados TLS.
- Atualizar o sistema e configurar firewall (UFW)
- Atualize e habilite o firewall, abrindo apenas portas essenciais:
sudo apt update && sudo apt upgrade -y
sudo apt install -y ufw
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status
- Instalar Docker Engine e Docker Compose (via repositório oficial)
- Adicione certificados e a chave GPG do Docker:
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
- Adicione o repositório “stable” do Docker (Ubuntu 24.04 = noble):
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
- Instale Docker e plugins:
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
- Verifique versões e adicione seu usuário ao grupo docker:
docker version
docker compose version
sudo usermod -aG docker $USER
newgrp docker
docker version
docker compose version
- Preparar diretórios do projeto
- Crie uma estrutura organizada para volumes, Nginx e Certbot:
mkdir -p ~/stack/{nginx/conf.d,certbot/{www,conf},data/{postgres,redis},data/n8n}
(Opcional) Rotação de logs do Docker
- Em servidores pequenos, controlar logs evita consumo de disco:
printf '{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }\n' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
sudo systemctl status docker
docker info | grep -A3 "Logging Driver"
- Arquivos da stack (Docker Compose e Nginx)
- Crie o arquivo docker-compose.yml em ~/stack/docker-compose.yml.
- Substitua:
- n8n.seu-dominio.com
- api.seu-dominio.com
- changeThisStrongPassword por uma senha forte
- seu-email@dominio.com (mais adiante, no Certbot)
Conteúdo do docker-compose.yml:
version: "3.9"
services:
postgres:
image: postgres:16-alpine
container_name: postgres
environment:
POSTGRES_USER: appuser
POSTGRES_PASSWORD: changeThisStrongPassword
POSTGRES_DB: n8n_db
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 512M
redis:
image: redis:7-alpine
container_name: redis
command: ["redis-server", "--appendonly", "yes", "--maxmemory", "128mb", "--maxmemory-policy", "allkeys-lru"]
volumes:
- ./data/redis:/data
networks:
- backend
restart: unless-stopped
deploy:
resources:
limits:
memory: 192M
n8n:
image: n8nio/n8n:latest
container_name: n8n
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
environment:
N8N_HOST: n8n.seu-dominio.com
N8N_PORT: 5678
WEBHOOK_URL: https://n8n.seu-dominio.com/
N8N_PROTOCOL: https
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: "5432"
DB_POSTGRESDB_DATABASE: n8n_db
DB_POSTGRESDB_USER: appuser
DB_POSTGRESDB_PASSWORD: changeThisStrongPassword
QUEUE_BULL_REDIS_HOST: redis
QUEUE_BULL_REDIS_PORT: "6379"
QUEUE_BULL_REDIS_DB: "0"
N8N_DIAGNOSTICS_ENABLED: "false"
N8N_VERSION_NOTIFICATIONS_ENABLED: "false"
GENERIC_TIMEZONE: "America/Sao_Paulo"
volumes:
- ./data/n8n:/home/node/.n8n
networks:
- backend
- proxy
restart: unless-stopped
deploy:
resources:
limits:
memory: 256M
evolution-api:
image: atendai/evolution-api:latest
container_name: evolution-api
depends_on:
redis:
condition: service_started
postgres:
condition: service_healthy
environment:
NODE_ENV: production
PORT: "3000"
DATABASE_CLIENT: pg
DATABASE_HOST: postgres
DATABASE_PORT: "5432"
DATABASE_NAME: evolution_db
DATABASE_USER: appuser
DATABASE_PASSWORD: changeThisStrongPassword
REDIS_HOST: redis
REDIS_PORT: "6379"
REDIS_DB: "1"
networks:
- backend
- proxy
restart: unless-stopped
deploy:
resources:
limits:
memory: 256M
nginx:
image: nginx:alpine
container_name: nginx
depends_on:
- n8n
- evolution-api
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt
ports:
- "80:80"
- "443:443"
networks:
- proxy
- backend
restart: unless-stopped
certbot:
image: certbot/certbot:latest
container_name: certbot
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
entrypoint: sh
command: -c "trap : TERM INT; sleep infinity & wait"
networks:
backend:
proxy:
- Crie o arquivo Nginx principal em ~/stack/nginx/nginx.conf:
user nginx;
worker_processes auto;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;
client_max_body_size 20m;
gzip on;
gzip_types text/plain text/css application/json application/javascript application/xml+rss;
include /etc/nginx/conf.d/*.conf;
}
- Crie o virtual host do n8n em ~/stack/nginx/conf.d/n8n.conf:
server {
listen 80;
server_name n8n.seu-dominio.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name n8n.seu-dominio.com;
ssl_certificate /etc/letsencrypt/live/n8n.seu-dominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.seu-dominio.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/n8n.seu-dominio.com/chain.pem;
location / {
proxy_pass http://n8n:5678/;
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 300;
}
}
- Crie o virtual host da API em ~/stack/nginx/conf.d/api.conf:
server {
listen 80;
server_name api.seu-dominio.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name api.seu-dominio.com;
ssl_certificate /etc/letsencrypt/live/api.seu-dominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.seu-dominio.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/api.seu-dominio.com/chain.pem;
location / {
proxy_pass http://evolution-api:3000/;
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 300;
}
}
- Subir Nginx e Certbot para emissão de certificados
- Com os DNS já apontados, suba Nginx e Certbot:
cd ~/stack
docker compose up -d nginx certbot
- Emita os certificados para cada domínio:
docker compose run --rm certbot certonly --webroot -w /var/www/certbot -d n8n.seu-dominio.com --email seu-email@dominio.com --agree-tos --no-eff-email
docker compose run --rm certbot certonly --webroot -w /var/www/certbot -d api.seu-dominio.com --email seu-email@dominio.com --agree-tos --no-eff-email
- Reinicie o Nginx para carregar os certificados:
docker compose restart nginx
- Subir toda a stack
- Agora, suba os serviços de aplicação:
docker compose up -d
- Verifique status:
docker compose ps
docker logs -f n8n
docker logs -f evolution-api
- Renovação automática dos certificados
- Agende uma tarefa diária (cron) para renovar e recarregar o Nginx:
echo '0 3 * * * docker compose -f /home/SEU_USUARIO/stack/docker-compose.yml run --rm certbot renew && docker compose -f /home/SEU_USUARIO/stack/docker-compose.yml exec nginx nginx -s reload' | crontab -
(Substitua SEU_USUARIO pelo seu usuário.)
- Segurança, performance e boas práticas
- Firewalls: mantenha apenas 22, 80 e 443 abertos.
- Não exponha PostgreSQL (5432) e Redis (6379) na internet; use apenas a rede interna do Docker.
- Memória: com 1 GB, limite os serviços (como no Compose) e monitore com docker stats. Se ocorrer OOM, considere ampliar RAM ou migrar Postgres para um serviço gerenciado.
- Backups:
- PostgreSQL: pg_dump e snapshots do volume ./data/postgres
- n8n: backup de ./data/n8n
- Variáveis do n8n: defina N8N_HOST, WEBHOOK_URL e timezone antes de criar credenciais e webhooks.
- Testes finais
- Acesse:
- Verifique o cadeado HTTPS e, se necessário, limpe cache do navegador.
- Cheque logs do Nginx: docker logs -f nginx
Por que essa abordagem?
- Docker isola serviços, facilita atualização e rollback.
- Nginx + Certbot oferece HTTPS automático e escalável.
- Compartilhar Postgres e Redis economiza recursos, mantendo separação lógica (bancos/dbs distintos).
- O repositório oficial do Docker para Ubuntu é confiável. O índice público exibe dists/ e o arquivo gpg (atualizado), garantindo a procedência da chave e dos pacotes.
Conclusão
Com este passo a passo, você levanta um ambiente produtivo e seguro para rodar n8n e Evolution API em uma VM modesta. Se quiser, posso gerar os arquivos já preenchidos com seus domínios e e-mail — basta me informar:
- Domínio do n8n
- Domínio da API
- E-mail para o Certbot
Bom deploy!
BONUS:
Instalar o htop para monitorar a utilização dos recursos do servidor e editor nano.
sudo apt update
sudo apt install -y htop nano
sudo apt update
sudo apt install cron -y
sudo systemctl enable –now cron
systemctl status cron
crontab -e
0 0,12 * * * cd /home/ubuntu/stack && docker compose run –rm certbot renew –quiet && docker compose exec nginx nginx -s reload
cd /home/ubuntu/stack
docker compose run –rm certbot renew –dry-run

