N8N v2 – Guia de Instalação

Saudações.

Esse tutorial é um guia rápido de instalação do N8N versão 2 modo fila com taskrunners.

Arquivos do guia:

Pré-requisitos (constam em outros artigos aqui do blog):

  • Instalação do Linux (Debian);
  • Internet no servidor (sua VPS ou host);
  • Docker CE instalado;
  • Traefik como proxy reverso;

1 – Sobre N8N versão 2

O N8N (eni eiti eni) é um sistema de construção de automações no-code e low-code usando fluxos visuais, ligando visualmente componentes uns aos outros para criar programas.

A versão 2 implementou algumas mudanças, entre elas, a mais drástica foi a separação da execução de códigos Python e Javascript em containers separados de sandbox que ele nomeou como taskrunners.

Diagrama de relação entre serviços (containers) na versão 2:

Task-Runner se conecta ao Worker para manifestar sua presença e extrair trabalhos de execução de código do usuário.

Com isso a estrutura de containers ficou assim:

  • editor (main): Roda o núcleo do N8N, gestão completa e disparo de eventos (triggers);
  • webhook: Roda o servidor web (HTTP) para disparo de eventos baseados na chamada de URLs e Webhooks (webhook trigger, form trigger);
  • worker: Detecta o disparo de eventos e inicia a execução do workflow node a node, é o serviço que faz o trabalho pesado, todos os nodes do tipo code de Javascript e Python são enviados para execução no taskrunner;
  • taskrunner: Se conecta ao worker para receber trabalhos de execução de códigos Javascript e Python, retorna os dados gerados e informações da execução;
  • redis: Provê o canal de comunicação pub-sub entre todos os serviços acima e serve de cache de dados;
  • postgresql: Provê o banco de dados persistente para cadastro de tudo (logins, credenciais, workflows, logs de execução, etc).

O serviço de webhook é opcional mas altamente recomendado para separar a carga de entrada das webhooks do editor, assim o editor fica com recursos reservados à administração do N8N enquanto que o webhook pode escalar por meio de réplicas.

Os containers dos 6 serviços tem diferentes naturezas de escalonamento:

  • editor (main): Por padrão não escala, não replicável, pode escalar por meio de licença especial que permite o ambiente multi-main que balanceia a carga HTTP das ações administrativas. Devido ao fato do main rodar o núcleo duro do N8N que dispara quase todos os tipos de triggers das automações, ao rodar várias instâncias do editor uma delas será eleita a principal (master) para assumir a tarefa de disparo de triggers enquanto as demais atual apenas como balanceamento de carga das chamadas ao backend vindas do frontend;
  • webhook: Replicável, o balanceamento de conexões de entrada é feito pelo Swarm e em casos mais avançados por cluster de proxy reverso;
  • worker: Replicável, o balanceamento da carga é feita internamente entre os workers que disputam serviços na mensageria do redis;
  • taskrunner: Replicável, o taskrunner requer o endereço do worker a qual ele se conectará e portanto o balanceamento é feito pelo Swarm;
  • redis: Não replicável, todos os containers do N8N precisam se comunicar por ele, para escalar o redis é necessário montar um cluster redis;
  • postgres: Não replicável, semelhante ao redis, requer cluster postgres separado;

2 – Preparativos

Nesse capítulo vou executar o N8N parte por parte para ambiente de estudos das partes isoladamente.

2.1 – Configurações

Personalize a variável EMAIL para que o LetsEncrypt funcione corretamente. Vou salvar o email de contato no arquivo /etc/email do host:

Bash
# Email de registro no letsencrypt
EMAIL="voce@seudominio.com.br";

# Gravar no arquivo para consulta
echo "$EMAIL" > /etc/email;

2.2 – Rede Docker

Rede Docker para containers do N8N.
Arquivo: /root/n8n-deploy/run-01-network-public.sh

/root/n8n-deploy/run-01-network-public.sh
# Rede Docker (bridge local)
docker network create \
    -d bridge \
    \
    -o "com.docker.network.bridge.name"="br-net-public" \
    -o "com.docker.network.bridge.enable_icc"="true" \
    -o "com.docker.network.driver.mtu"="1500" \
    \
    --subnet 10.249.0.0/16 --gateway 10.249.255.254 \
    \
    network_public;

2.3 – Proxy Reverso – Traefik

Container do Traefik para proxy reverso com HTTPs automático.
Arquivo: /root/n8n-deploy/run-02-traefic-app.sh

/root/n8n-deploy/run-02-traefic-app.sh
# Email de contato para o certificado LetsEncrypt:
    EMAIL="voce@seudominio.com.br";
    [ -f /etc/email ] && EMAIL=$(head -1 /etc/email);

# Imagem do traefik, baixar atualizada:
    docker pull traefik:latest;

# Diretorio de dados persistentes:
    mkdir -p /storage/traefik-app/letsencrypt;
    mkdir -p /storage/traefik-app/logs;
    mkdir -p /storage/traefik-app/config;

# Renovar/rodar:
  docker rm -f traefik-app 2>/dev/null;
  docker run \
    -d --restart=always \
    --name traefik-app -h traefik-app.intranet.br \
    --tmpfs /run:rw,noexec,nosuid,size=8m \
    --tmpfs /tmp:rw,noexec,nosuid,size=8m \
    --read-only \
    --cpus="8.0" --memory=4g --memory-swap=4g \
    \
    --network network_public \
    \
    -p 80:80 \
    -p 443:443 \
    \
    -v /var/run/docker.sock:/var/run/docker.sock:ro \
    -v /storage/traefik-app/letsencrypt:/etc/letsencrypt \
    -v /storage/traefik-app/config:/etc/traefik \
    -v /storage/traefik-app/logs:/logs \
    \
    traefik:latest \
      \
      --global.checkNewVersion=false \
      --global.sendAnonymousUsage=false \
      \
      --api.insecure=true \
      \
      --log.level=INFO \
      --log.filePath=/logs/error.log \
      \
      --accessLog.filePath=/logs/access.log \
      \
      --entrypoints.web.address=:80 \
      --entrypoints.web.http.redirections.entryPoint.to=websecure \
      --entrypoints.web.http.redirections.entryPoint.scheme=https \
      --entrypoints.web.http.redirections.entryPoint.permanent=true \
      --entrypoints.websecure.address=:443 \
      \
      --providers.docker=true \
      --providers.file.directory=/etc/traefik \
      \
      --certificatesresolvers.letsencrypt.acme.email=$EMAIL \
      --certificatesresolvers.letsencrypt.acme.storage=/etc/letsencrypt/acme.json \
      --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web;

2.4 – Banco de dados PostgreSQL

Vamos criar um banco de dados PostgreSQL chamado “n8n-pgsql“.
Arquivo: /root/n8n-deploy/run-03-n8n-postgresql.sh

Bash
# Credenciais de acesso ao PG
    POSTGRES_USER="postgres";
    POSTGRES_PASSWORD="tulipasql";
    POSTGRES_DB="n8n";

# Pasta para o volume
    mkdir -p /storage/n8n-pgsql;
    chown -R 999:999 /storage/n8n-pgsql;

# Rodar:
    docker pull pgvector/pgvector:pg18-trixie;
    docker run \
        -d --restart=always \
        --name n8n-pgsql -h n8n-pgsql.intranet.br \
        --read-only --cpus="2.0" --memory=2g --memory-swap=2g --shm-size=1g \
        \
        --network network_public \
        \
        --tmpfs /run:rw,noexec,nosuid,size=128m \
        --tmpfs /tmp:rw,noexec,nosuid,size=128m \
        \
        -v /storage/n8n-pgsql:/var/lib/postgresql/data \
        \
        -e POSTGRES_USER=$POSTGRES_USER \
        -e POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
        -e POSTGRES_DB=$POSTGRES_DB \
        -e PGDATA=/var/lib/postgresql/data/pgdata \
        \
        --health-cmd="pg_isready -U postgres" \
        --health-interval=5s \
        --health-timeout=5s \
        --health-retries=10 \
        \
        --entrypoint "docker-entrypoint.sh" \
        \
        pgvector/pgvector:pg18-trixie \
            postgres \
                --max_connections=8192 \
                --wal_level=minimal \
                --max_wal_senders=0 \
                --port=5432;

2.5 – Redis

O Redis é vital para o N8N, vamos criar o container chamado “n8n-redis“.
Arquivo: /root/n8n-deploy/run-04-n8n-redis.sh

Bash
# Pasta de persistencia RDB e AOF:
    mkdir -p /storage/n8n-redis;
    chown -R 999:999 /storage/n8n-redis;

# Baixar imagem atualizada do Redis (https://hub.docker.com/_/redis):
    docker pull redis:latest;

# Remover container atual:
    docker rm -f n8n-redis 2>/dev/null;
    
# Criar container do redis:
    docker run \
        -d --restart=always \
        --name n8n-redis -h n8n-redis.intranet.br \
        --read-only --cpus="1.0" --memory=1g --memory-swap=1g \
        \
        --network network_public \
        \
        -v /storage/n8n-redis:/data \
        -w /data \
        \
        --health-cmd="redis-cli ping" \
        --health-interval=1s \
        --health-timeout=3s \
        \
        redis:latest \
            redis-server \
                --tcp-backlog 8192 --tcp-keepalive 30 --timeout 0 \
                --dir /data --save 16 1 --save 12 10 --save  6 100 \
                --rdbcompression no --appendonly yes --appendfsync everysec;

3 – Variáveis de ambiente

O N8N possui uma vastidão de variáveis de ambientes mas elas podem ser divididas de acordo com suas finalidades e quais serviços precisam delas.

Variáveis com valor semelhante ao valor padrão estarão em AZUL e os valores alterados e personalizados de VERMELHO.

Você pode omitir todas as variáveis em AZUL sem problemas, isso economiza espaço na configuração.

3.1 – Modo fila (1 variável)

O antigo modo simples baseado no sqlite foi substituído pelo modo fila que faz uso do Redis e PostgreSQL por meio da técnica Claim Check Pattern (sinais em tempo real no Redis pub/sub e dados de transações no SQL).

Dados para acesso ao Redis e PG do modelo fila foram definidos na criação dos containers no capítulo 2.

Usadas pelo editor, webhook e worker, não são usadas pelo task-runner.

VariávelValor
EXECUTIONS_MODEqueue
EXECUTIONS_TIMEOUT
Padrão -1 (eterno), limite em segundos, evita acumulo de workflows travados
Valor em segundos
1800
EXECUTIONS_TIMEOUT_MAX
Recomendável 7200 (2h) para execuções longas
Valor em segundos
3600

3.2 – Acesso a Redis (1 variável)

Usados pelo editor, webhook e worker, não é usado pelo task-runner.

Variáveis do Redis:

VariávelValor
QUEUE_BULL_REDIS_HOSTn8n-redis
QUEUE_BULL_REDIS_CLUSTER_NODES
Quando houver cluster redis, sintaxe: redis-1:6379,redis-2:6379
(vazio)
QUEUE_BULL_REDIS_DNS_LOOKUP_STRATEGYLOOKUP
QUEUE_BULL_REDIS_PORT6379
QUEUE_BULL_REDIS_TLSfalse
QUEUE_BULL_REDIS_DB
Altere para usar um Redis compartilhado com outros sistemas
0
QUEUE_BULL_REDIS_KEEP_ALIVEfalse
QUEUE_BULL_REDIS_KEEP_ALIVE_DELAY5000
QUEUE_BULL_REDIS_KEEP_ALIVE_INTERVAL5000
QUEUE_BULL_REDIS_RECONNECT_ON_FAILOVERtrue
QUEUE_BULL_REDIS_USERNAME(vazio)
QUEUE_BULL_REDIS_PASSWORD(vazio)
QUEUE_BULL_REDIS_TIMEOUT_THRESHOLD10_000
QUEUE_BULL_REDIS_SLOT_REFRESH_TIMEOUT1_000
QUEUE_BULL_REDIS_SLOT_REFRESH_INTERVAL5_000
QUEUE_BULL_PREFIX
Prefixo das chaves de cache
bull
N8N_GRACEFUL_SHUTDOWN_TIMEOUT30
QUEUE_BULL_REDIS_DUALSTACK
Se alterado para true, permite uso de IPv6 se presente
false
QUEUE_HEALTH_CHECK_ACTIVE
Mude para true se o Redis sofre risco de sair do ar
false
QUEUE_HEALTH_CHECK_PORT5678
N8N_REDIS_KEY_PREFIXn8n
QUEUE_WORKER_LOCK_DURATION60_000
QUEUE_WORKER_LOCK_RENEW_TIME10_000
QUEUE_WORKER_STALLED_INTERVAL30_000

3.3 – Acesso a Postgres (3 variáveis)

Usados pelo editor, webhook e worker, não é usado pelo task-runner.

VariávelValor
DB_TYPEpostgresdb
DB_POSTGRESDB_HOSTn8n-pgsql
DB_POSTGRESDB_PORT5432
DB_POSTGRESDB_DATABASEn8n
DB_POSTGRESDB_USERpostgres
DB_POSTGRESDB_PASSWORDtulipasql
DB_POSTGRESDB_SCHEMApublic
DB_POSTGRESDB_POOL_SIZE
Tuning: Recomendo aumentar para 32
2
DB_POSTGRESDB_SSL_ENABLEDfalse
DB_POSTGRESDB_SSL_CA(vazio)
DB_POSTGRESDB_SSL_CERT(vazio)
DB_POSTGRESDB_SSL_KEY(vazio)
DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZEDtrue
DB_POSTGRESDB_CONNECTION_TIMEOUT
Valor em milissegundos, timeout com o PG
20_000
DB_POSTGRESDB_IDLE_CONNECTION_TIMEOUT
Valor em milissegundos, encerra conexões adicionais inativas
30_000
DB_POSTGRESDB_STATEMENT_TIMEOUT
Valor em milissegundos, limite de execução de uma query
300_000

O N8N não envia operações de vaccum para o postgres, lembre de fazer essa rotina mensalmente.

3.4 – Opções globais (2 variáveis)

Variáveis de uso global e necessárias para todos os serviços: editor, webhook, worker, e task-runner.

VariávelValor
GENERIC_TIMEZONE
Padrão: America/New_York
America/Sao_Paulo
TZ
Padrão herdado do sistema local
America/Sao_Paulo

3.5 – Opções gerais (5 variáveis)

Variáveis usadas pelo editor, webhook e worker, não são usadas no task-runner.

VariávelValor
N8N_PATH/
N8N_DEFAULT_LOCALEen
N8N_ENCRYPTION_KEY
Padrão vazio, usado para protege credenciais
tulipa
NODE_ENV
Requer especificação, opções: test, development ou production
production
N8N_DIAGNOSTICS_ENABLED
Padrão true mas não vamos enviar dados para a N8N
false
N8N_USER_FOLDER
Padrão $HOME do usuário node
/data
N8N_LOG_LEVELinfo
N8N_LOG_CRON_ACTIVE_INTERVAL0
N8N_LOG_FILE_COUNT_MAX100
N8N_LOG_FILE_SIZE_MAX16
N8N_LOG_FILE_LOCATIONlogs/n8n.log
N8N_LOG_OUTPUTconsole,file
N8N_LOG_FORMATtext
N8N_LOG_SCOPES(vazio)
N8N_DIAGNOSTICS_POSTHOG_API_KEY(chave da n8n)
N8N_DIAGNOSTICS_POSTHOG_API_HOST(url posthog)
N8N_DIAGNOSTICS_CONFIG_FRONTEND(url da n8n)
N8N_DIAGNOSTICS_CONFIG_BACKEND(url da n8n)
N8N_METRICStrue
N8N_METRICS_PREFIXn8n_
N8N_METRICS_INCLUDE_DEFAULT_METRICStrue
N8N_METRICS_INCLUDE_WORKFLOW_ID_LABELfalse
N8N_METRICS_INCLUDE_NODE_TYPE_LABELfalse
N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABELfalse
N8N_METRICS_INCLUDE_API_ENDPOINTSfalse
N8N_METRICS_INCLUDE_API_PATH_LABELfalse
N8N_METRICS_INCLUDE_API_METHOD_LABELfalse
N8N_METRICS_INCLUDE_API_STATUS_CODE_LABELfalse
N8N_METRICS_INCLUDE_CACHE_METRICSfalse
N8N_METRICS_INCLUDE_MESSAGE_EVENT_BUS_METRICSfalse
N8N_METRICS_INCLUDE_QUEUE_METRICSfalse
N8N_METRICS_QUEUE_METRICS_INTERVAL20
N8N_METRICS_ACTIVE_WORKFLOW_METRIC_INTERVAL60
N8N_METRICS_INCLUDE_WORKFLOW_NAME_LABELfalse
N8N_METRICS_INCLUDE_WORKFLOW_STATISTICSfalse
N8N_METRICS_WORKFLOW_STATISTICS_INTERVAL300
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONStrue
N8N_CACHE_BACKEND
Opções: memory, redis, auto (padrão, prefere Redis)
auto
N8N_CACHE_MEMORY_MAX_SIZE
Tamanho máximo de mensagens na memória em bytes
3145728
N8N_CACHE_MEMORY_TTL
Tempo de vida em segundos de mensagens na memória em ms
3600000
N8N_CACHE_REDIS_KEY_PREFIX
Prefixo da chave no Redis para mensagens na memória
cache
N8N_HIRING_BANNER_ENABLED
Desative apenas se estiver em modo desenvolvedor
true

3.6 – Opções de servidor http (4 variáveis)

Variáveis usadas apenas pelos serviços que abrem a porta HTTP para webhooks.

Usados pelo editor e webhook, não é usado pelo worker e task-runner.

VariávelValor
WEBHOOK_URL(a explicar)
N8N_HOST(a explicar)
N8N_PORT5678
N8N_LISTEN_ADDRESS::
N8N_PROTOCOL
Padrão: http
https
N8N_PROXY_HOPS
Padrão: 0
1
N8N_SSL_CERT(vazio)
N8N_SSL_KEY(vazio)
N8N_PAYLOAD_SIZE_MAX
Valor em MB
16
N8N_FORMDATA_FILE_SIZE_MAX200

3.6 – Opções exclusivas do editor (7 variáveis)

Usados somente pelo editor (frontend e backend).

VariávelValor
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS
Será transformada em hard coded em breve, pinar para deixar pronto
true
N8N_EDITOR_BASE_URL(a explicar)
N8N_DISABLE_UIfalse
N8N_SECURE_COOKIE
Para permitir acesso HTTP direto na porta do N8N,
deixe em true (padrão) ou omita para restringir o acesso apenas por HTTPs
false
N8N_AI_PROVIDERopenai
N8N_AI_OPENAI_API_KEY(vazio)
N8N_AI_TIMEOUT_MAX
Padrão 3.600.000 ms (1h, muito tempo), recomendo 900000 (15min)
900000
N8N_AI_ALLOW_SENDING_PARAMETER_VALUEStrue
N8N_ENDPOINT_RESTrest
N8N_ENDPOINT_FORMform
N8N_ENDPOINT_FORM_TESTform-test
N8N_ENDPOINT_FORM_WAITform-waiting
N8N_ENDPOINT_WEBHOOKwebhook
N8N_ENDPOINT_WEBHOOK_TESTwebhook-test
N8N_ENDPOINT_WEBHOOK_WAITwebhook-waiting
N8N_ENDPOINT_MCPmcp
N8N_ENDPOINT_MCP_TESTmcp-test
N8N_DISABLE_PRODUCTION_MAIN_PROCESSfalse
N8N_ADDITIONAL_NON_UI_ROUTES(vazio)
N8N_ENDPOINT_HEALTH/healthz
EXECUTIONS_DATA_HARD_DELETE_BUFFER
Valor em horas
1
EXECUTIONS_DATA_MAX_AGE
Valor em horas
336
EXECUTIONS_DATA_PRUNEtrue
EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL
Valor em minutos
15
EXECUTIONS_DATA_PRUNE_MAX_COUNT10_000
EXECUTIONS_DATA_PRUNE_SOFT_DELETE_INTERVAL
Valor em minutos
60
EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONStrue
N8N_VERSION_NOTIFICATIONS_ENABLED
Desativa notificações sobre novas versões do N8N
false
N8N_VERSION_NOTIFICATIONS_ENDPOINT(url da n8n)
N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENABLED
Desativa notificações de novos recursos dos updates recentes
false
N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENDPOINT(url da n8n)
N8N_VERSION_NOTIFICATIONS_INFO_URL(url da n8n)
N8N_PUBLIC_API_DISABLEDfalse
N8N_PUBLIC_API_ENDPOINTapi
N8N_PUBLIC_API_SWAGGERUI_DISABLEDtrue
N8N_PUSH_BACKEND
Padrão websocket bidirecional ou sse para unidirecional
websocket
WORKFLOWS_DEFAULT_NAMEMy workflow
N8N_TEMPLATES_ENABLEDtrue
N8N_TEMPLATES_HOST(url n8n)
N8N_DYNAMIC_TEMPLATES_HOST(url n8n)

3.7 – Opções do editor e worker (3 variáveis)

Usados pelo editor e worker, não é usado pelo webhook e task-runner.

VariávelValor
N8N_RUNNERS_MODEexternal
EXECUTIONS_DATA_SAVE_ON_ERROR
Opções: all, none, padrão all
all
EXECUTIONS_DATA_SAVE_ON_PROGRESS
Padrão: false
true
EXECUTIONS_DATA_SAVE_ON_SUCCESS
Opções: all, none, padrão none
all
N8N_COMMUNITY_PACKAGES_ENABLEDtrue
N8N_COMMUNITY_PACKAGES_REGISTRYhttps://registry.npmjs.org
N8N_REINSTALL_MISSING_PACKAGESfalse
N8N_UNVERIFIED_PACKAGES_ENABLEDtrue
N8N_VERIFIED_PACKAGES_ENABLEDtrue
N8N_COMMUNITY_PACKAGES_PREVENT_LOADINGfalse
N8N_PYTHON_ENABLEDtrue

3.8 – Opções exclusivas do worker (2 variáveis)

Variáveis usadas somente pelo worker.

VariávelValor
N8N_RUNNERS_BROKER_LISTEN_ADDRESS
Padrão 127.0.0.1, não escuta na rede local
0.0.0.0
N8N_RUNNERS_ENABLED
Legado da v2, descontinuada na v2 e hard-coded como true
true
N8N_CONCURRENCY_PRODUCTION_LIMIT
Execuções ilimitadas em -1 ou especifique o limite de paralelismo por worker
-1
N8N_CONCURRENCY_EVALUATION_LIMIT-1
CODE_ENABLE_STDOUT
Mude para true para permitir que node Code use a saida stdout
false

3.9 – Opções exclusivas do task-runner (2 variáveis)

Variáveis usadas pelo task-runner.

VariávelValor
N8N_RUNNERS_TASK_BROKER_URI(a explicar)
N8N_RUNNERS_HEALTH_CHECK_SERVER_ENABLEDtrue
N8N_RUNNERS_HEALTH_CHECK_SERVER_HOSTtrue
N8N_RUNNERS_HEALTH_CHECK_SERVER_PORTtrue
N8N_RUNNERS_GRANT_TOKENtrue
N8N_RUNNERS_MAX_PAYLOAD
Padrão: 1024 * 1024 * 1024 = 1.073.741.824 bytes (1 GB)
1073741824
N8N_RUNNERS_MAX_CONCURRENCY
Padrão: 10, causa gargalos em burst de tarefas
128
N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT0
N8N_RUNNERS_PATH/runners
N8N_RUNNERS_BROKER_PORT5679
N8N_RUNNERS_MAX_OLD_SPACE_SIZE(vazio)
N8N_RUNNERS_TASK_TIMEOUT300
N8N_RUNNERS_TASK_REQUEST_TIMEOUT60
N8N_RUNNERS_HEARTBEAT_INTERVAL30
N8N_RUNNERS_INSECURE_MODEfalse
N8N_RUNNERS_LAUNCHER_LOG_LEVELdebug

3.9 – Opções do worker e task-runner (2 variáveis)

Usados pelo worker e task-runner.

VariávelValor
N8N_RUNNERS_AUTH_TOKEN
Padrão vazio, especificar por segurança
tulipa
N8N_RUNNERS_ENABLED
Legado da v2, descontinuada na v2 e hard-coded como true
true

3.10 – Opções de gerenciamento de usuários e SMTP (4 variáveis)

Usados pelo editor para envio de emails (notificações, convites, recuperação de senha).

VariávelValor
N8N_EMAIL_MODEsmtp
N8N_SMTP_HOST
Endereço do servidor SMTP
(informe)
N8N_SMTP_PORT
Porta do protocolo SMTP, 465=SMTP+SSL, 587=SMTP+StartTLS
465
N8N_SMTP_SSL
Padrão true, altere para false se for usar a porta 587
true
N8N_SMTP_STARTTLS
Padrão true, altere para false se for usar a porta 465
true
N8N_SMTP_SENDER
Email de remetente original
(informe)
N8N_SMTP_USER
Nome de usuário no serviço SMTP
(informe)
N8N_SMTP_PASS
Senha de autenticação SMTP
(informe)
N8N_SMTP_OAUTH_SERVICE_CLIENT(vazio)
N8N_SMTP_OAUTH_PRIVATE_KEY(vazio)
N8N_UM_EMAIL_TEMPLATES_PWRESET3600
N8N_UM_EMAIL_TEMPLATES_INVITE3600
N8N_UM_EMAIL_TEMPLATES_WORKFLOW_SHARED(vazio)
N8N_UM_EMAIL_TEMPLATES_WORKFLOW_AUTODEACTIVATED(vazio)
N8N_UM_EMAIL_TEMPLATES_CREDENTIALS_SHARED(vazio)
N8N_UM_EMAIL_TEMPLATES_PROJECT_SHARED(vazio)
N8N_UM_EMAIL_TEMPLATES_WORKFLOW_FAILURE(vazio)
N8N_INVITE_LINKS_EMAIL_ONLYfalse
N8N_USER_MANAGEMENT_JWT_SECRET(vazio)
N8N_USER_MANAGEMENT_JWT_DURATION_HOURS168
N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS0
N8N_SMTP_OAUTH_SERVICE_CLIENT(vazio)
N8N_SMTP_OAUTH_PRIVATE_KEY(vazio)

3.11 – Opções da licença enterprise (4 variáveis)

Quando a licença enterprise é ativada você pode usar novos recursos localmente.
Use esse link para adquirir a licença: https://n8n.io/enterprise/

As variáveis para instalar a licença devem ser informadas em todos os serviços do n8n ( editor, worker e webhook, menos o task-runner:

VariávelValor
N8N_LICENSE_ACTIVATION_KEY(chave aqui)
N8N_LICENSE_AUTO_RENEW_ENABLEDtrue
N8N_LICENSE_DETACH_FLOATING_ON_SHUTDOWNtrue
N8N_LICENSE_TENANT_ID1
N8N_LICENSE_CERT(vazio)
N8N_LICENSE_SERVER_URL(url da n8n)

As variáveis de controle de recursos enterprise são:

VariávelValor
N8N_AI_ENABLEDtrue
N8N_AI_ASSISTANT_BASE_URL(url chatbot)
N8N_AI_ANTHROPIC_KEY(sua chave)
N8N_AI_PERSIST_BUILDER_SESSIONStrue
N8N_PERSONALIZATION_ENABLEDtrue
LANGSMITH_API_KEY(vazio)
LANGSMITH_TRACINGtrue
LANGSMITH_MINIMAL_TRACINGfalse
LANGSMITH_ENDPOINT(url LS)

4 – Entendendo o Task-Runner

O runner executará em container separado, esse modo é chamado de EXTERNAL.

Diferenças:

  • internal: O task-runner é executado como processo filho do processo que executa o workflow;
    • Vantagens: simples, rápido;
    • Desvantagens: inseguro, pode quebrar o sistema inteiro;
  • external: O task-runner é executado em serviço separado e isolado, recebe apenas os dados objetivos para execução de código JS e PY e cria um sandbox;
    • Vantagens: seguro, isolado, resiliente;
    • Desvantagens: lento, podem induzir uma demora de até 3 segundos na execução do workflow;

Vou abordar o modo external pois é a tendência de evolução do N8N.

Quando o worker é iniciado com a configuração N8N_RUNNERS_MODE=external ele precisará abrir a porta 5679/tcp para atuar como servidor broker:

  • Porta em N8N_RUNNERS_BROKER_PORT=5679;
  • IP de escuta em N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0;

O worker não poderá executar nenhum “node code” até que 1 runner se concte a ele.

Essa lógica permite que os containers dos runners trabalhem remotamente em servidores diferentes dos servidores de worker, editor e webhook.

O task-runner precisa saber o endereço IP e porta do worker a qual ele se conectará para coletar trabalho, o endereço é definido na variável:

Vários runners podem apontar para um mesmo worker.

O ideal é trabalhar com no mínimo 2 réplicas de workers e 4 réplicas de runners.

5 – Endereços de acesso

O N8N pode ser executado em ambiente privado sem HTTPs ou em ambiente público com homologação completa (HTTPs + Certificado reconhecido globalmente).

5.1 – Executando N8N em ambiente privado

As seguintes variáveis devem ser configuradas para acessar o N8N diretamente na porta HTTP (sem criptografia) e sem URL pública:

VariávelValor
N8N_HOST
IPv4, IPv6 ou nome de DNS do servidor
192.168.14.199
WEBHOOK_URL
URL do servidor webhook
http://192.168.14.199
N8N_PROXY_HOPS
Sem proxy-reverso entre o navegador e o N8N
0
N8N_EDITOR_BASE_URL
URL para geração de links nas automações
http://192.168.14.199
N8N_SECURE_COOKIE
Para permitir acesso HTTP direto na porta do N8N
false

O acesso será na porta HTTP direta do N8N (N8N_PORT=5678).
Exemplo de acesso ao editor: http://192.168.14.199:5678/

O container de webhook precisará de outra porta redirecionada para a mesma porta 5678 ou ser publicada por meio de um túnel (CloudFlare, Akamai, webhook-relay).

5.2 – Executando N8N em ambiente público

Será necessário configurar o DNS corretamente, os seguintes nomes serão necessários:

Nome de DNS (FQDN)Serviço
n8n.seudominio.com.brEditor do N8N – Administração
ws.seudominio.com.brWebhook – Servidor HTTP das automações
Troque os nomes sugeridos pelos nomes desejados no seu domínio.

As variáveis devem ser preenchidas assim:

VariávelValor
N8N_HOST
IPv4, IPv6 ou nome de DNS do servidor
n8n.seudominio.com.br
WEBHOOK_URL
URL do servidor webhook
https://ws.seudominio.com.br
N8N_PROXY_HOPS
Com proxy-reverso entre o navegador e o N8N
1
N8N_EDITOR_BASE_URL
URL para geração de links nas automações
https://n8n.seudominio.com.br
Troque os nomes sugeridos pelos nomes desejados no seu domínio.

O acesso será na porta HTTPs (tcp/443) do proxy-reverso (Traefik) que encaminhará para o container do editor na porta http 5678 (N8N_PORT=5678).

Exemplo de acesso ao editor: https://n8n.seudominio.com.br

Exemplo de acesso ao webhook: https://ws.seudominio.com.br

5.3 – Sufixos dos serviços webhook

O servidor de webhook responde pelas URLs providas pelas automações e faz a separação por pasta na URL, acessos:

  • Trigger webhook: Sufixo padrão “webhook“, pode ser personalizado na variável N8N_ENDPOINT_WEBHOOK.
    • URL: https://ws.seudominio.com.br/webhook/
    • Alternativa: definir como N8N_EDITOR_BASE_URL=v1, fica assim:
      • URL: https://ws.seudominio.com.br/v1/
  • Trigger MCP-Server: Sufixo padrão “mcp“, pode ser personalizado na variável N8N_ENDPOINT_MCP.
    • URL: https://ws.seudominio.com.br/mcp/
  • Trigger Form: Sufixo padrão “form“, pode ser personalizado na variável N8N_ENDPOINT_FORM.
    • URL: https://ws.seudominio.com.br/form/

Não altere a menos que seja proposital e planejado pois pode quebrar automações antigas e ou importadas.

6 – Executando o N8N

Vamos partir para a criação dos serviços com tudo que organizamos até aqui usando o N8N em uma URL pública (HTTPs provido por Traefik e LetsEncrypt).

6.1 – Pasta do projeto

Crie uma pasta para o projeto, nele iremos colocar todos os arquivos necessários para rodar independente do estilo (eu prefiro “docker run”).

Diretório: /root/n8n-deploy:

Bash
# Diretorio do projeto
mkdir -p /root/n8n-deploy;

6.2 – Pasta dos volumes

Você pode usar qualquer volume em qualquer diretório, o importante é fixar o UID/GID da pasta principal do volume para 1000 (usuário node dentro do container).

Arquivo: /root/n8n-deploy/run-11-datadir.sh

/root/n8n-deploy/run-11-datadir.sh
# Pasta para volume dos containers
    mkdir -p /storage/n8n-app;
    mkdir -p /storage/n8n-app/editor;
    mkdir -p /storage/n8n-app/worker;
    mkdir -p /storage/n8n-app/webhook;
    mkdir -p /storage/n8n-app/runner;

# Corrigir permissoes:
    chown -R 1000:1000 /storage/n8n-app;

6.3 – Versão do N8N

Vou usar a versão estável 2.11.3 na tag docker.n8n.io/n8nio/n8n:2.11.3, é importante evitar a tag “docker.n8n.io/n8nio/n8n:latest” pois podem haver updates que quebram funcionalidades.

Imagens:

  • N8N: docker.n8n.io/n8nio/n8n:2.11.3
  • Runner: n8nio/runners:2.11.3

Arquivo: /root/n8n-deploy/run-12-pull-image.sh

/root/n8n-deploy/run-12-pull-image.sh
# Baixar imagem docker do N8N
    docker pull docker.n8n.io/n8nio/n8n:2.11.3;

# Baixar imagem docker do Runner
    docker pull n8nio/runners:2.11.3;

6.4 – Arquivos .env

Vamos criar um arquivo .env para cada grupo de variáveis baseado nos serviços que farão leitura deles, assim teremos uma configuração mais pontual e segura.

Variáveis que afetam todos os serviços.
Arquivo: /root/n8n-deploy/.env-n8n-global

/root/n8n-deploy/.env-n8n-global
GENERIC_TIMEZONE=America/Sao_Paulo
TZ=America/Sao_Paulo

Variáveis de todos os serviços para modo queue.
Arquivo: /root/n8n-deploy/.env-n8n-queue

/root/n8n-deploy/.env-n8n-queue
EXECUTIONS_MODE=queue
EXECUTIONS_TIMEOUT=1800

Variáveis de acesso ao Redis.
Arquivo: /root/n8n-deploy/.env-n8n-redis

/root/n8n-deploy/.env-n8n-redis
QUEUE_BULL_REDIS_HOST=n8n-redis

Variáveis de acesso ao Postgres.
Arquivo: /root/n8n-deploy/.env-n8n-postgres

/root/n8n-deploy/.env-n8n-postgres
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=n8n-pgsql
DB_POSTGRESDB_PASSWORD=tulipasql

Variáveis de todos os serviços do N8N.
Arquivo: /root/n8n-deploy/.env-n8n-services

/root/n8n-deploy/.env-n8n-services
N8N_ENCRYPTION_KEY=tulipa
NODE_ENV=production
N8N_DIAGNOSTICS_ENABLED=false
N8N_USER_FOLDER=/data
N8N_METRICS=true

Variáveis exclusivas do editor.
Arquivo: /root/n8n-deploy/.env-n8n-editor

/root/n8n-deploy/.env-n8n-editor
N8N_EDITOR_BASE_URL=https://n8n.seudominio.com.br/
N8N_SECURE_COOKIE=false
N8N_AI_TIMEOUT_MAX=900000
N8N_VERSION_NOTIFICATIONS_ENABLED=false
N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENABLED=false
N8N_PUBLIC_API_SWAGGERUI_DISABLED=true
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true

Variáveis de serviços WEB (editor e webhook).
Arquivo: /root/n8n-deploy/.env-n8n-web

/root/n8n-deploy/.env-n8n-web
WEBHOOK_URL=https://ws.seudominio.com.br/
N8N_HOST=n8n.seudominio.com.br
N8N_PROTOCOL=https
N8N_PROXY_HOPS=1

Variáveis dos serviços de workflow (editor e worker).
Arquivo: /root/n8n-deploy/.env-n8n-core

/root/n8n-deploy/.env-n8n-core
N8N_RUNNERS_MODE=external
EXECUTIONS_DATA_SAVE_ON_PROGRESS=true
EXECUTIONS_DATA_SAVE_ON_SUCCESS=all

Variáveis exclusivas do worker.
Arquivo: /root/n8n-deploy/.env-n8n-worker

/root/n8n-deploy/.env-n8n-worker
N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
N8N_RUNNERS_ENABLED=true

Variáveis exclusivas do task-runner.
Arquivo: /root/n8n-deploy/.env-n8n-runner

/root/n8n-deploy/.env-n8n-runner
N8N_RUNNERS_TASK_BROKER_URI=http://n8n-worker:5679
N8N_RUNNERS_MAX_CONCURRENCY=128

Variáveis para comunicação entre editor, worker e task-runner.
Arquivo: /root/n8n-deploy/.env-n8n-tasks

/root/n8n-deploy/.env-n8n-tasks
N8N_RUNNERS_AUTH_TOKEN=tulipa

Variáveis para

gestão de usuários e envio de emails, usadas somente pelo editor.
Arquivo: /root/n8n-deploy/.env-n8n-manager

/root/n8n-deploy/.env-n8n-manager
N8N_SMTP_HOST=smtp.seudominio.com.br
N8N_SMTP_USER=voce@seudominio.com.br
N8N_SMTP_PASS=sua-senha-segura
N8N_SMTP_SENDER=voce@seudominio.com.br

Variáveis para recursos enterprise, usadas apenas no editor, worker e worker.
Arquivo: /root/n8n-deploy/.env-n8n-enterprise

/root/n8n-deploy/.env-n8n-enterprise
N8N_AI_ENABLED=true
N8N_AI_ASSISTANT_BASE_URL=https://n8n-ai-agent.seudominio.com.br
N8N_AI_ANTHROPIC_KEY=sk-ant-....sua-chave-aqui...
N8N_AI_PERSIST_BUILDER_SESSIONS=true

6.5 – Executando o editor (main)

Script no arquivo /root/n8n-deploy/run-21-editor.sh

/root/n8n-deploy/run-21-editor.sh
# Nome de DNS para acesso HTTPs
    # - Importar da variavel N8N_HOST no arquivo .env
    . /root/n8n-deploy/.env-n8n-web;

# Remover container atual:
    docker rm -f n8n-editor 2>/dev/null;
  
# Rodando:
    docker run -d \
        --name n8n-editor -h n8n-editor.intranet.br \
        --cpus=4 --memory=4g --memory-swap=4g --shm-size=1g \
        --tmpfs /run:rw,noexec,nosuid,size=512m \
        --tmpfs /tmp:rw,noexec,nosuid,size=512m \
        \
        --network network_public \
        \
        --env-file /root/n8n-deploy/.env-n8n-global \
        --env-file /root/n8n-deploy/.env-n8n-queue \
        --env-file /root/n8n-deploy/.env-n8n-redis \
        --env-file /root/n8n-deploy/.env-n8n-postgres \
        --env-file /root/n8n-deploy/.env-n8n-services \
        --env-file /root/n8n-deploy/.env-n8n-manager \
        --env-file /root/n8n-deploy/.env-n8n-enterprise \
        \
        --env-file /root/n8n-deploy/.env-n8n-editor \
        --env-file /root/n8n-deploy/.env-n8n-web \
        --env-file /root/n8n-deploy/.env-n8n-core \
        --env-file /root/n8n-deploy/.env-n8n-tasks \
        \
        -v /storage/n8n-app/editor:/data \
        \
        --label "traefik.enable=true" \
        --label "traefik.http.routers.n8n-editor.rule=Host(\`$N8N_HOST\`)" \
        --label "traefik.http.routers.n8n-editor.entrypoints=web,websecure" \
        --label "traefik.http.routers.n8n-editor.tls=true" \
        --label "traefik.http.routers.n8n-editor.tls.certresolver=letsencrypt" \
        --label "traefik.http.services.n8n-editor.loadbalancer.server.port=5678" \
        \
        docker.n8n.io/n8nio/n8n:2.11.3;

6.6 – Executando o webhook

Execução muito parecida com a do editor, difere pelo comando “webhook” no container. A o nome para redirecionamento no Traefik será retirado da variável WEBHOOK_URL.
Script no arquivo /root/n8n-deploy/run-22-webhook.sh

/root/n8n-deploy/run-22-webhook.sh
# Nome de DNS para acesso HTTPs
    # - Importar da variavel N8N_HOST no arquivo .env
    . /root/n8n-deploy/.env-n8n-web;
    FQDN_WEBHOOK=$(echo $WEBHOOK_URL | cut -f3 -d/);

# Remover container atual:
    docker rm -f n8n-webhook 2>/dev/null;

# Rodando:
    docker run -d \
        --name n8n-webhook -h n8n-webhook.intranet.br \
        --cpus=4 --memory=4g --memory-swap=4g --shm-size=1g \
        --tmpfs /run:rw,noexec,nosuid,size=512m \
        --tmpfs /tmp:rw,noexec,nosuid,size=512m \
        \
        --network network_public \
        \
        --env-file /root/n8n-deploy/.env-n8n-global \
        --env-file /root/n8n-deploy/.env-n8n-queue \
        --env-file /root/n8n-deploy/.env-n8n-redis \
        --env-file /root/n8n-deploy/.env-n8n-postgres \
        --env-file /root/n8n-deploy/.env-n8n-services \
        --env-file /root/n8n-deploy/.env-n8n-enterprise \
        \
        --env-file /root/n8n-deploy/.env-n8n-web \
        \
        -v /storage/n8n-app/webhook:/data \
        \
        --label "traefik.enable=true" \
        --label "traefik.http.routers.n8n-webhook.rule=Host(\`$FQDN_WEBHOOK\`)" \
        --label "traefik.http.routers.n8n-webhook.entrypoints=web,websecure" \
        --label "traefik.http.routers.n8n-webhook.tls=true" \
        --label "traefik.http.routers.n8n-webhook.tls.certresolver=letsencrypt" \
        --label "traefik.http.services.n8n-webhook.loadbalancer.server.port=5678" \
        \
        docker.n8n.io/n8nio/n8n:2.11.3 webhook;

6.7 – Executando o worker

O worker precisa se conectar ao Redis, Postgres e aguardar o Task-Runner se conectar nele. O container deve rodar o comando “worker“.
Script no arquivo /root/n8n-deploy/run-23-worker.sh

/root/n8n-deploy/run-23-worker.sh
# Remover container atual:
    docker rm -f n8n-worker 2>/dev/null;

# Rodando:
    docker run -d \
        --name n8n-worker -h n8n-worker.intranet.br \
        --cpus=4 --memory=4g --memory-swap=4g --shm-size=1g \
        --tmpfs /run:rw,noexec,nosuid,size=512m \
        --tmpfs /tmp:rw,noexec,nosuid,size=512m \
        \
        --network network_public \
        \
        --env-file /root/n8n-deploy/.env-n8n-global \
        --env-file /root/n8n-deploy/.env-n8n-queue \
        --env-file /root/n8n-deploy/.env-n8n-redis \
        --env-file /root/n8n-deploy/.env-n8n-postgres \
        --env-file /root/n8n-deploy/.env-n8n-services \
        --env-file /root/n8n-deploy/.env-n8n-enterprise \
        \
        --env-file /root/n8n-deploy/.env-n8n-core \
        --env-file /root/n8n-deploy/.env-n8n-worker \
        --env-file /root/n8n-deploy/.env-n8n-tasks \
        \
        -v /storage/n8n-app/worker:/data \
        \
        docker.n8n.io/n8nio/n8n:2.11.3 worker;

6.8 – Executando o task-runner

O task-runner requer uma atenção especial à variável N8N_RUNNERS_TASK_BROKER_URI que deve apontar para o nome do container do worker na porta 5679.
Script no arquivo /root/n8n-deploy/run-24-runner.sh

/root/n8n-deploy/run-24-runner.sh
# Remover container atual:
    docker rm -f n8n-runner 2>/dev/null;

# Rodando:
    docker run -d \
        --name n8n-runner -h n8n-runner.intranet.br \
        --cpus=4 --memory=4g --memory-swap=4g --shm-size=1g \
        --tmpfs /run:rw,noexec,nosuid,size=512m \
        --tmpfs /tmp:rw,noexec,nosuid,size=512m \
        \
        --network network_public \
        \
        --env-file /root/n8n-deploy/.env-n8n-global \
        \
        --env-file /root/n8n-deploy/.env-n8n-runner \
        --env-file /root/n8n-deploy/.env-n8n-tasks \
        \
        -v /storage/n8n-app/runner:/data \
        \
         n8nio/runners:2.11.3;

6.9 – Execução final

Rode os scripts:

Bash
# Rodar scripts para implementar N8N:
bash "/root/n8n-deploy/run-01-network-public.sh";
bash "/root/n8n-deploy/run-02-traefic-app.sh";
bash "/root/n8n-deploy/run-03-n8n-postgresql.sh";
bash "/root/n8n-deploy/run-04-n8n-redis.sh";
bash "/root/n8n-deploy/run-11-datadir.sh";
bash "/root/n8n-deploy/run-12-pull-image.sh";
bash "/root/n8n-deploy/run-21-editor.sh";
bash "/root/n8n-deploy/run-22-webhook.sh";
bash "/root/n8n-deploy/run-23-worker.sh";
bash "/root/n8n-deploy/run-24-runner.sh";

Agora todos os serviços estão rodando.

Exportando arquivos do tutorial em formato MarkDown.
Arquivo (execute após criar): /root/n8n-deploy/export-markdown.sh

/root/n8n-deploy/export-markdown.sh
# Exportar projeto em markdown
(
    echo '# N8N v2 Deploy';
    echo;
    echo '## Arquivos ENV';
    for envfile in /root/n8n-deploy/.env*; do
        echo "### $envfile";
        echo;
        echo '```env'; cat $envfile; echo '```';
        echo;
    done;
    echo;
    echo '## Scripts';
    for script in /root/n8n-deploy/run*; do
        echo "### $script";
        echo;
        echo '```bash'; cat $script; echo '```';
        echo;
    done;
    echo;
) > /root/n8n-deploy/README.md;

7 – Primeiro contato

Após rodar os containers, abra o navegador no endereço https://n8n.seudominio.com.br para cadastrar o login de administrador.

Você pode automatizar esse setup de usuário administrador.

Arquivo (execute após criar): /root/n8n-deploy/run-51-setup-admin.sh

/root/n8n-deploy/run-51-setup-admin.sh
# Nome de DNS para acesso HTTPs
    # - Importar da variavel N8N_HOST no arquivo .env
    . /root/n8n-deploy/.env-n8n-web;

# Dados do formulario
    # Template JSON
    JSON_DATA='{
        "email": "admin@acme.com",
        "firstName": "Acme",
        "lastName": "Jobs",
        "password": "Acme@123"
    }';

    echo "# Definir login administrativo:";
    echo;
    echo "# URL: https://$N8N_HOST/rest/owner/setup";
    echo;
    echo "$JSON_DATA";
    echo;

# Acionar API:
    curl --insecure \
        -X POST \
        -H 'Accept: application/json, text/plain, */*' \
        -H 'Content-Type: application/json' \
        -d "$JSON_DATA" \
        -o "/tmp/n8n-instance-setup.json" \
        "https://$N8N_HOST/rest/owner/setup";

8 – Stack para Docker Compose standalone

Agora vamos empacotar nosso setup para simplificar a implementação usando Docker Compose em um servidor sem swarm, ou seja, o Docker standalone.

8.1 – Preparativos – Rede e Traefik

Nessa stack vamos colocar o Traefik (container traefik-app) e a rede Docker (network_public). Troque o e-mail voce@seudomino.com.br pelo seu e-mail verdadeiro.

Arquivo: /root/n8n-deploy/docker-compose-traefik-standalone.yml

/root/n8n-deploy/docker-compose-traefik-standalone.yml
networks:
  network_public:
    name: network_public
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: "br-net-public"
      com.docker.network.bridge.enable_icc: "true"
      com.docker.network.driver.mtu: "1500"
    ipam:
      driver: default
      config:
        - subnet: 10.249.0.0/16
          gateway: 10.249.255.254

services:
  traefik-app:
    image: traefik:latest
    container_name: traefik-app
    hostname: traefik-app.intranet.br
    restart: always
    cpus: 4
    mem_limit: 4g
    memswap_limit: 4g
    shm_size: 1g
    read_only: true
    tmpfs:
      - /run:rw,noexec,nosuid,size=2m
      - /tmp:rw,noexec,nosuid,size=2m
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /storage/traefik-app/letsencrypt:/etc/letsencrypt
      - /storage/traefik-app/config:/etc/traefik
      - /storage/traefik-app/logs:/logs
    networks:
      - network_public
    command:
      # Global
      - "--global.checkNewVersion=false"
      - "--global.sendAnonymousUsage=false"

      # API / Dashboard (acesso inseguro - use apenas em ambiente controlado)
      - "--api.insecure=true"

      # Logs de erro
      - "--log.level=INFO"
      - "--log.filePath=/logs/error.log"

      # Logs de acesso
      - "--accessLog.filePath=/logs/access.log"

      # Entrypoint HTTP (porta 80) com redirect automático para HTTPS
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entryPoint.permanent=true"

      # Entrypoint HTTPS (porta 443)
      - "--entrypoints.websecure.address=:443"

      # Providers
      - "--providers.docker=true"
      - "--providers.file.directory=/etc/traefik"

      # Certificados Let's Encrypt via HTTP Challenge
      - "--certificatesresolvers.letsencrypt.acme.email=voce@seudominio.com.br"
      - "--certificatesresolvers.letsencrypt.acme.storage=/etc/letsencrypt/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"

Aplicar stack:

Bash
# Implementar Traefik e rede network_public:
docker compose -f /root/n8n-deploy/docker-compose-traefik-standalone.yml up -d;
    # [+] up 2/2
    #  ✔ Network network_public Created
    #  ✔ Container traefik-app  Created

# Para destruir:
# docker compose -f /root/n8n-deploy/docker-compose-traefik.yml down;

8.2 – Stack básica dos scripts

Essa stack tem os mesmos argumentos dos scripts anteriores, fazendo referência aos arquivos .env-* das variáveis de ambiente e usando volumes montados em /storage/.

Para rodar com sucesso, crie os arquivos .env-* e a stack abaixo.

Troque os nomes de DNS de seudomino.com.br pelo seus nomes.

Arquivo: /root/n8n-deploy/docker-compose-n8n-standalone.yml

/root/n8n-deploy/docker-compose-n8n-standalone.yml
networks:
  network_public:
    external: true

x-n8n-common: &n8n-common
  image: docker.n8n.io/n8nio/n8n:2.11.3
  restart: always
  cpus: 4
  mem_limit: 4g
  memswap_limit: 4g
  shm_size: 1g
  tmpfs:
    - /run:rw,noexec,nosuid,size=512m
    - /tmp:rw,noexec,nosuid,size=512m
  networks:
    - network_public
  env_file:
    - .env-n8n-global
    - .env-n8n-queue
    - .env-n8n-redis
    - .env-n8n-postgres
    - .env-n8n-services

services:

  # ───────────────────────────────────────── PostgreSQL

  n8n-pgsql:
    image: pgvector/pgvector:pg18-trixie
    container_name: n8n-pgsql
    hostname: n8n-pgsql.intranet.br
    restart: always
    read_only: true
    cpus: "2.0"
    mem_limit: 2g
    memswap_limit: 2g
    shm_size: 1g
    networks:
      - network_public
    tmpfs:
      - /run:rw,noexec,nosuid,size=128m
      - /tmp:rw,noexec,nosuid,size=128m
    volumes:
      - /storage/n8n-pgsql:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: tulipasql
      POSTGRES_DB: n8n
      PGDATA: /var/lib/postgresql/data/pgdata
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      timeout: 5s
      retries: 10
    entrypoint: ["docker-entrypoint.sh"]
    command:
      - postgres
      - --max_connections=8192
      - --wal_level=minimal
      - --max_wal_senders=0
      - --port=5432

  # ───────────────────────────────────────── Redis

  n8n-redis:
    image: redis:latest
    container_name: n8n-redis
    hostname: n8n-redis.intranet.br
    restart: always
    read_only: true
    cpus: "1.0"
    mem_limit: 1g
    memswap_limit: 1g
    working_dir: /data
    networks:
      - network_public
    volumes:
      - /storage/n8n-redis:/data
    command: >
      redis-server
        --tcp-backlog 8192
        --tcp-keepalive 30
        --timeout 0
        --dir /data
        --save 16 1
        --save 12 10
        --save 6 100
        --rdbcompression no
        --appendonly yes
        --appendfsync everysec
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 5

  # ───────────────────────────────────────── N8N Editor

  n8n-editor:
    <<: *n8n-common
    container_name: n8n-editor
    hostname: n8n-editor.intranet.br
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    env_file:
      - .env-n8n-global
      - .env-n8n-queue
      - .env-n8n-redis
      - .env-n8n-postgres
      - .env-n8n-services
      - .env-n8n-manager
      - .env-n8n-enterprise
      - .env-n8n-editor
      - .env-n8n-web
      - .env-n8n-core
      - .env-n8n-tasks
    volumes:
      - /storage/n8n-app/editor:/data
    labels:
      traefik.enable: "true"
      traefik.http.routers.n8n-editor.rule: "Host(`n8n.seudominio.com.br`)"
      traefik.http.routers.n8n-editor.entrypoints: "web,websecure"
      traefik.http.routers.n8n-editor.tls: "true"
      traefik.http.routers.n8n-editor.tls.certresolver: "letsencrypt"
      traefik.http.services.n8n-editor.loadbalancer.server.port: "5678"

  # ───────────────────────────────────────── N8N Webhook

  n8n-webhook:
    <<: *n8n-common
    container_name: n8n-webhook
    hostname: n8n-webhook.intranet.br
    command: webhook
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    env_file:
      - .env-n8n-global
      - .env-n8n-queue
      - .env-n8n-redis
      - .env-n8n-postgres
      - .env-n8n-services
      - .env-n8n-enterprise
      - .env-n8n-web
    volumes:
      - /storage/n8n-app/webhook:/data
    labels:
      traefik.enable: "true"
      traefik.http.routers.n8n-webhook.rule: "Host(`ws.seudominio.com.br`)"
      traefik.http.routers.n8n-webhook.entrypoints: "web,websecure"
      traefik.http.routers.n8n-webhook.tls: "true"
      traefik.http.routers.n8n-webhook.tls.certresolver: "letsencrypt"
      traefik.http.services.n8n-webhook.loadbalancer.server.port: "5678"

  # ───────────────────────────────────────── N8N Worker

  n8n-worker:
    <<: *n8n-common
    container_name: n8n-worker
    hostname: n8n-worker.intranet.br
    command: worker
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    env_file:
      - .env-n8n-global
      - .env-n8n-queue
      - .env-n8n-redis
      - .env-n8n-postgres
      - .env-n8n-services
      - .env-n8n-enterprise
      - .env-n8n-core
      - .env-n8n-worker
      - .env-n8n-tasks
    volumes:
      - /storage/n8n-app/worker:/data

  # ───────────────────────────────────────── N8N Task Runner

  n8n-runner:
    image: n8nio/runners:2.11.3
    container_name: n8n-runner
    hostname: n8n-runner.intranet.br
    restart: always
    cpus: 4
    mem_limit: 4g
    memswap_limit: 4g
    shm_size: 1g
    tmpfs:
      - /run:rw,noexec,nosuid,size=512m
      - /tmp:rw,noexec,nosuid,size=512m
    networks:
      - network_public
    env_file:
      - /root/n8n-deploy/.env-n8n-global
      - /root/n8n-deploy/.env-n8n-runner
      - /root/n8n-deploy/.env-n8n-tasks
    volumes:
      - /storage/n8n-app/runner:/data

Aplicar stack:

Bash
# Implementar N8N standalone:
docker compose -f /root/n8n-deploy/docker-compose-n8n-standalone.yml up -d;
    # [+] up 6/6
    #  ✔ Container n8n-redis   Healthy  1.8s 
    #  ✔ Container n8n-runner  Created  0.1s 
    #  ✔ Container n8n-pgsql   Healthy  5.8s 
    #  ✔ Container n8n-worker  Created  0.0s 
    #  ✔ Container n8n-editor  Created  0.0s 
    #  ✔ Container n8n-webhook Created   0.0s 

# Para destruir:
# docker compose -f /root/n8n-deploy/docker-compose-n8n-standalone.yml down;

8.3 – Stack final com variáveis incorporadas

Essa é a minha técnica favorita pois me permite criar um único arquivo do compose para o serviço. Preservei os nomes dos containers e os volumes montados.

Arquivo: /root/n8n-deploy/docker-compose-n8n-std-inline.yml

/root/n8n-deploy/docker-compose-n8n-std-inline.yml
# ----------------------------------------- ENVs

x-env-global: &env-global
  GENERIC_TIMEZONE: America/Sao_Paulo
  TZ: America/Sao_Paulo

x-env-queue: &env-queue
  EXECUTIONS_MODE: queue
  EXECUTIONS_TIMEOUT: "1800"

x-env-redis: &env-redis
  QUEUE_BULL_REDIS_HOST: n8n-redis

x-env-postgres: &env-postgres
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: n8n-pgsql
  DB_POSTGRESDB_PASSWORD: tulipasql

x-env-services: &env-services
  N8N_ENCRYPTION_KEY: tulipa
  NODE_ENV: production
  N8N_DIAGNOSTICS_ENABLED: "false"
  N8N_USER_FOLDER: /data
  N8N_METRICS: "true"

x-env-manager: &env-manager
  N8N_SMTP_HOST: smtp.seudominio.com.br
  N8N_SMTP_USER: voce@seudominio.com.br
  N8N_SMTP_PASS: sua-senha-segura
  N8N_SMTP_SENDER: voce@seudominio.com.br

x-env-enterprise: &env-enterprise
  N8N_AI_ENABLED: "true"
  N8N_AI_ASSISTANT_BASE_URL: https://n8n-ai-agent.seudominio.com.br
  N8N_AI_ANTHROPIC_KEY: sk-ant-....sua-chave-aqui...
  N8N_AI_PERSIST_BUILDER_SESSIONS: "true"

x-env-core: &env-core
  N8N_RUNNERS_MODE: external
  EXECUTIONS_DATA_SAVE_ON_PROGRESS: "true"
  EXECUTIONS_DATA_SAVE_ON_SUCCESS: all

x-env-editor: &env-editor
  N8N_EDITOR_BASE_URL: https://n8n.seudominio.com.br/
  N8N_SECURE_COOKIE: false
  N8N_AI_TIMEOUT_MAX: 900000
  N8N_VERSION_NOTIFICATIONS_ENABLED: "false"
  N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENABLED: "false"
  N8N_PUBLIC_API_SWAGGERUI_DISABLED: "true"
  OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"

x-env-web: &env-web
  WEBHOOK_URL: https://ws.seudominio.com.br/
  N8N_HOST: n8n.seudominio.com.br
  N8N_PROTOCOL: https
  N8N_PROXY_HOPS: "1"

x-env-tasks: &env-tasks
  N8N_RUNNERS_AUTH_TOKEN: tulipa

x-env-worker: &env-worker
  N8N_RUNNERS_BROKER_LISTEN_ADDRESS: 0.0.0.0
  N8N_RUNNERS_ENABLED: "true"

x-env-runner: &env-runner
  N8N_RUNNERS_TASK_BROKER_URI: http://n8n-worker:5679
  N8N_RUNNERS_MAX_CONCURRENCY: 128


# ----------------------------------------- Shared Sets

x-n8n-common: &n8n-common
  image: docker.n8n.io/n8nio/n8n:2.11.3
  restart: always
  cpus: 4
  mem_limit: 4g
  memswap_limit: 4g
  shm_size: 1g
  tmpfs:
    - /run:rw,noexec,nosuid,size=512m
    - /tmp:rw,noexec,nosuid,size=512m
  networks:
    - network_public


# ----------------------------------------- Deploy

networks:
  network_public:
    external: true

services:

  # ----------------------------------------- PostgreSQL

  n8n-pgsql:
    image: pgvector/pgvector:pg18-trixie
    container_name: n8n-pgsql
    hostname: n8n-pgsql.intranet.br
    restart: always
    read_only: true
    cpus: "2.0"
    mem_limit: 2g
    memswap_limit: 2g
    shm_size: 1g
    networks:
      - network_public
    tmpfs:
      - /run:rw,noexec,nosuid,size=128m
      - /tmp:rw,noexec,nosuid,size=128m
    volumes:
      - /storage/n8n-pgsql:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: tulipasql
      POSTGRES_DB: n8n
      PGDATA: /var/lib/postgresql/data/pgdata
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      timeout: 5s
      retries: 10
    entrypoint: ["docker-entrypoint.sh"]
    command:
      - postgres
      - --max_connections=8192
      - --wal_level=minimal
      - --max_wal_senders=0
      - --port=5432

  # ----------------------------------------- Redis

  n8n-redis:
    image: redis:latest
    container_name: n8n-redis
    hostname: n8n-redis.intranet.br
    restart: always
    read_only: true
    cpus: "1.0"
    mem_limit: 1g
    memswap_limit: 1g
    working_dir: /data
    networks:
      - network_public
    volumes:
      - /storage/n8n-redis:/data
    command: >
      redis-server
        --tcp-backlog 8192
        --tcp-keepalive 30
        --timeout 0
        --dir /data
        --save 16 1
        --save 12 10
        --save 6 100
        --rdbcompression no
        --appendonly yes
        --appendfsync everysec
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 5

  # ----------------------------------------- N8N Editor
  #   env: global + queue + redis + postgres + services
  #      + editor + web + core + tasks

  n8n-editor:
    <<: *n8n-common
    container_name: n8n-editor
    hostname: n8n-editor.intranet.br
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres,
           *env-services, *env-manager, *env-enterprise,
           *env-editor, *env-web, *env-core, *env-tasks]
    volumes:
      - /storage/n8n-app/editor:/data
    labels:
      traefik.enable: "true"
      traefik.http.routers.n8n-editor.rule: "Host(`n8n.seudominio.com.br`)"
      traefik.http.routers.n8n-editor.entrypoints: "web,websecure"
      traefik.http.routers.n8n-editor.tls: "true"
      traefik.http.routers.n8n-editor.tls.certresolver: "letsencrypt"
      traefik.http.services.n8n-editor.loadbalancer.server.port: "5678"

  # ----------------------------------------- N8N Webhook
  #   env: global + queue + redis + postgres + services + web

  n8n-webhook:
    <<: *n8n-common
    container_name: n8n-webhook
    hostname: n8n-webhook.intranet.br
    command: webhook
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres,
           *env-services, *env-web]
    volumes:
      - /storage/n8n-app/webhook:/data
    labels:
      traefik.enable: "true"
      traefik.http.routers.n8n-webhook.rule: "Host(`ws.seudominio.com.br`)"
      traefik.http.routers.n8n-webhook.entrypoints: "web,websecure"
      traefik.http.routers.n8n-webhook.tls: "true"
      traefik.http.routers.n8n-webhook.tls.certresolver: "letsencrypt"
      traefik.http.services.n8n-webhook.loadbalancer.server.port: "5678"

  # ----------------------------------------- N8N Worker
  #   env: global + queue + redis + postgres + services
  #      + core + worker + tasks

  n8n-worker:
    <<: *n8n-common
    container_name: n8n-worker
    hostname: n8n-worker.intranet.br
    command: worker
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres,
           *env-services, *env-core, *env-worker, *env-tasks]
    volumes:
      - /storage/n8n-app/worker:/data

  # ----------------------------------------- N8N Task Runner
  #   env: global + runner + tasks

  n8n-runner:
    image: n8nio/runners:2.11.3
    container_name: n8n-runner
    hostname: n8n-runner.intranet.br
    restart: always
    cpus: 4
    mem_limit: 4g
    memswap_limit: 4g
    shm_size: 1g
    tmpfs:
      - /run:rw,noexec,nosuid,size=512m
      - /tmp:rw,noexec,nosuid,size=512m
    networks:
      - network_public
    environment:
      <<: [*env-global, *env-runner, *env-tasks]
    volumes:
      - /storage/n8n-app/runner:/data

Aplicar stack:

Bash
# Implementar N8N standalone inline:
docker compose -f /root/n8n-deploy/docker-compose-n8n-std-inline.yml up -d;
    # [+] up 6/6
    #  ✔ Container n8n-redis   Healthy  1.8s 
    #  ✔ Container n8n-runner  Created  0.1s 
    #  ✔ Container n8n-pgsql   Healthy  5.8s 
    #  ✔ Container n8n-worker  Created  0.0s 
    #  ✔ Container n8n-editor  Created  0.0s 
    #  ✔ Container n8n-webhook Created   0.0s 

# Para destruir:
# docker compose -f /root/n8n-deploy/docker-compose-n8n-std-inline.yml down;

8.4 – Stack final incorporada sem task-runner

Bom… o task-runner, modo external, adiciona segurança e uma latência muito irritante na execução dos códigos Javascript e Python. Enquanto ainda é possível usar o modo internal, a melhor opção para performance é abrir mão da segurança.

As alterações para usar o modo legado internal são:

  • Nas variáveis de .env-n8n-core (env-core), mudar para:
    • N8N_RUNNERS_MODE=internal
    • N8N_RUNNERS_ENABLED=true
  • As variáveis de .env-n8n-worker não serão necessárias, desconsiderar:
    • N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
    • N8N_RUNNERS_ENABLED=true
  • Não precisa rodar o serviço (container) do task-runner (sem .env-n8n-runner).

Problemas:

  • Se o código Javascript abusar da memória RAM o container pode travar;
  • Códigos em Python 3 não irão funcionar (somente com runner);

Nova stack:
Arquivo: /root/n8n-deploy/docker-compose-n8n-std-inline-internal.yml

/root/n8n-deploy/docker-compose-n8n-std-inline-internal.yml
# ----------------------------------------- ENVs

x-env-global: &env-global
  GENERIC_TIMEZONE: America/Sao_Paulo
  TZ: America/Sao_Paulo

x-env-queue: &env-queue
  EXECUTIONS_MODE: queue
  EXECUTIONS_TIMEOUT: "1800"

x-env-redis: &env-redis
  QUEUE_BULL_REDIS_HOST: n8n-redis

x-env-postgres: &env-postgres
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: n8n-pgsql
  DB_POSTGRESDB_PASSWORD: tulipasql

x-env-services: &env-services
  N8N_ENCRYPTION_KEY: tulipa
  NODE_ENV: production
  N8N_DIAGNOSTICS_ENABLED: "false"
  N8N_USER_FOLDER: /data
  N8N_METRICS: "true"

x-env-manager: &env-manager
  N8N_SMTP_HOST: smtp.seudominio.com.br
  N8N_SMTP_USER: voce@seudominio.com.br
  N8N_SMTP_PASS: sua-senha-segura
  N8N_SMTP_SENDER: voce@seudominio.com.br

x-env-enterprise: &env-enterprise
  N8N_AI_ENABLED: "true"
  N8N_AI_ASSISTANT_BASE_URL: https://n8n-ai-agent.seudominio.com.br
  N8N_AI_ANTHROPIC_KEY: sk-ant-....sua-chave-aqui...
  N8N_AI_PERSIST_BUILDER_SESSIONS: "true"

x-env-core: &env-core
  N8N_RUNNERS_MODE: internal
  N8N_RUNNERS_ENABLED: "true"
  EXECUTIONS_DATA_SAVE_ON_PROGRESS: "true"
  EXECUTIONS_DATA_SAVE_ON_SUCCESS: all

x-env-editor: &env-editor
  N8N_EDITOR_BASE_URL: https://n8n.seudominio.com.br/
  N8N_SECURE_COOKIE: false
  N8N_AI_TIMEOUT_MAX: 900000
  N8N_VERSION_NOTIFICATIONS_ENABLED: "false"
  N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENABLED: "false"
  N8N_PUBLIC_API_SWAGGERUI_DISABLED: "true"
  OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"

x-env-web: &env-web
  WEBHOOK_URL: https://ws.seudominio.com.br/
  N8N_HOST: n8n.seudominio.com.br
  N8N_PROTOCOL: https
  N8N_PROXY_HOPS: "1"

x-env-tasks: &env-tasks
  N8N_RUNNERS_AUTH_TOKEN: tulipa


# ----------------------------------------- Shared Sets

x-n8n-common: &n8n-common
  image: docker.n8n.io/n8nio/n8n:2.11.3
  restart: always
  cpus: 4
  mem_limit: 4g
  memswap_limit: 4g
  shm_size: 1g
  tmpfs:
    - /run:rw,noexec,nosuid,size=512m
    - /tmp:rw,noexec,nosuid,size=512m
  networks:
    - network_public


# ----------------------------------------- Deploy

networks:
  network_public:
    external: true

services:

  # ----------------------------------------- PostgreSQL

  n8n-pgsql:
    image: pgvector/pgvector:pg18-trixie
    container_name: n8n-pgsql
    hostname: n8n-pgsql.intranet.br
    restart: always
    read_only: true
    cpus: "2.0"
    mem_limit: 2g
    memswap_limit: 2g
    shm_size: 1g
    networks:
      - network_public
    tmpfs:
      - /run:rw,noexec,nosuid,size=128m
      - /tmp:rw,noexec,nosuid,size=128m
    volumes:
      - /storage/n8n-pgsql:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: tulipasql
      POSTGRES_DB: n8n
      PGDATA: /var/lib/postgresql/data/pgdata
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      timeout: 5s
      retries: 10
    entrypoint: ["docker-entrypoint.sh"]
    command:
      - postgres
      - --max_connections=8192
      - --wal_level=minimal
      - --max_wal_senders=0
      - --port=5432

  # ----------------------------------------- Redis

  n8n-redis:
    image: redis:latest
    container_name: n8n-redis
    hostname: n8n-redis.intranet.br
    restart: always
    read_only: true
    cpus: "1.0"
    mem_limit: 1g
    memswap_limit: 1g
    working_dir: /data
    networks:
      - network_public
    volumes:
      - /storage/n8n-redis:/data
    command: >
      redis-server
        --tcp-backlog 8192
        --tcp-keepalive 30
        --timeout 0
        --dir /data
        --save 16 1
        --save 12 10
        --save 6 100
        --rdbcompression no
        --appendonly yes
        --appendfsync everysec
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 5

  # ----------------------------------------- N8N Editor
  #   env: global + queue + redis + postgres + services
  #      + editor + web + core + tasks

  n8n-editor:
    <<: *n8n-common
    container_name: n8n-editor
    hostname: n8n-editor.intranet.br
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres,
           *env-services, *env-manager, *env-enterprise,
           *env-editor, *env-web, *env-core, *env-tasks]
    volumes:
      - /storage/n8n-app/editor:/data
    labels:
      traefik.enable: "true"
      traefik.http.routers.n8n-editor.rule: "Host(`n8n.seudominio.com.br`)"
      traefik.http.routers.n8n-editor.entrypoints: "web,websecure"
      traefik.http.routers.n8n-editor.tls: "true"
      traefik.http.routers.n8n-editor.tls.certresolver: "letsencrypt"
      traefik.http.services.n8n-editor.loadbalancer.server.port: "5678"

  # ----------------------------------------- N8N Webhook
  #   env: global + queue + redis + postgres + services + web

  n8n-webhook:
    <<: *n8n-common
    container_name: n8n-webhook
    hostname: n8n-webhook.intranet.br
    command: webhook
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres,
           *env-services, *env-web]
    volumes:
      - /storage/n8n-app/webhook:/data
    labels:
      traefik.enable: "true"
      traefik.http.routers.n8n-webhook.rule: "Host(`ws.seudominio.com.br`)"
      traefik.http.routers.n8n-webhook.entrypoints: "web,websecure"
      traefik.http.routers.n8n-webhook.tls: "true"
      traefik.http.routers.n8n-webhook.tls.certresolver: "letsencrypt"
      traefik.http.services.n8n-webhook.loadbalancer.server.port: "5678"

  # ----------------------------------------- N8N Worker
  #   env: global + queue + redis + postgres + services
  #      + core + tasks

  n8n-worker:
    <<: *n8n-common
    container_name: n8n-worker
    hostname: n8n-worker.intranet.br
    command: worker
    depends_on:
      n8n-pgsql:
        condition: service_healthy
      n8n-redis:
        condition: service_healthy
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres,
           *env-services, *env-core, *env-tasks]
    volumes:
      - /storage/n8n-app/worker:/data

Aplicar stack:

Bash
# Implementar N8N standalone inline:
docker compose -f /root/n8n-deploy/docker-compose-n8n-std-inline-internal.yml up -d;
    # [+] up 6/6
    #  ✔ Container n8n-redis   Healthy  1.8s 
    #  ✔ Container n8n-pgsql   Healthy  5.8s 
    #  ✔ Container n8n-worker  Created  0.0s 
    #  ✔ Container n8n-editor  Created  0.0s 
    #  ✔ Container n8n-webhook Created   0.0s 

# Para destruir:
# docker compose -f /root/n8n-deploy/docker-compose-n8n-std-inline-internal.yml down;

9 – Stack para Docker Swarm

Agora vem a parte divertida: rodar o N8N com escala horizontal.

Para isso a stack sofrerá as seguintes alterações:

  • Prefixo: no Swarm as stacks tem um nome de serviço que é usado como prefixo para o nome dos objetos (rede, serviços, volumes), assim simplifica-se o nome na stack.
  • Limite de recursos: sessão “deploy” da stack;
  • Rede: sem IP especificado para evitar conflitos;
  • Réplicas: o serviços worker, webhook e runners serão replicados 4 vezes cada, você pode diminuir ou aumentar de acordo com sua carga de trabalho.

Arquivo: /root/n8n-deploy/docker-compose-swarm.yml

docker stack deploy -c docker-compose-swarm.yml n8n

/root/n8n-deploy/docker-compose-swarm.yml

x-env-global: &env-global
  GENERIC_TIMEZONE: America/Sao_Paulo
  TZ: America/Sao_Paulo

x-env-queue: &env-queue
  EXECUTIONS_MODE: queue
  EXECUTIONS_TIMEOUT: "1800"

x-env-redis: &env-redis
  QUEUE_BULL_REDIS_HOST: redis

x-env-postgres: &env-postgres
  DB_TYPE: postgresdb
  DB_POSTGRESDB_HOST: pgsql
  DB_POSTGRESDB_PASSWORD: tulipasql

x-env-services: &env-services
  N8N_ENCRYPTION_KEY: tulipa
  NODE_ENV: production
  N8N_DIAGNOSTICS_ENABLED: "false"
  N8N_USER_FOLDER: /data
  N8N_METRICS: "true"

x-env-editor: &env-editor
  N8N_EDITOR_BASE_URL: https://n8n.cloud05.tmsoft.com.br/
  N8N_SECURE_COOKIE: false
  N8N_AI_TIMEOUT_MAX: 900000
  N8N_VERSION_NOTIFICATIONS_ENABLED: "false"
  N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENABLED: "false"
  N8N_PUBLIC_API_SWAGGERUI_DISABLED: "true"
  OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS: "true"

x-env-web: &env-web
  WEBHOOK_URL: https://ws.cloud05.tmsoft.com.br/
  N8N_HOST: n8n.cloud05.tmsoft.com.br
  N8N_PROTOCOL: https
  N8N_PROXY_HOPS: "1"

x-env-core: &env-core
  N8N_RUNNERS_MODE: external
  EXECUTIONS_DATA_SAVE_ON_PROGRESS: "true"
  EXECUTIONS_DATA_SAVE_ON_SUCCESS: all

x-env-tasks: &env-tasks
  N8N_RUNNERS_AUTH_TOKEN: tulipa

x-env-worker: &env-worker
  N8N_RUNNERS_BROKER_LISTEN_ADDRESS: 0.0.0.0
  N8N_RUNNERS_ENABLED: "true"

x-env-runner: &env-runner
  N8N_RUNNERS_TASK_BROKER_URI: http://worker:5679
  N8N_RUNNERS_MAX_CONCURRENCY: 128

x-n8n-common: &n8n-common
  image: docker.n8n.io/n8nio/n8n:2.11.3
  networks:
    - intranet

networks:
  intranet:
    name: intranet
    driver: overlay
    attachable: true
    driver_opts:
      com.docker.network.driver.mtu: "1450"

volumes:
  pgsql:
  redis:
  editor:
  webhook:
  worker:
  runner:

services:

  # ----------------------------------------- PostgreSQL

  pgsql:
    image: pgvector/pgvector:pg18-trixie
    read_only: true
    networks:
      - intranet
    volumes:
      - pgsql:/var/lib/postgresql/data
      - type: tmpfs
        target: /run
        tmpfs:
          size: 134217728
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 134217728
      - type: tmpfs
        target: /dev/shm
        tmpfs:
          size: 1073741824
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: tulipasql
      POSTGRES_DB: n8n
      PGDATA: /var/lib/postgresql/data/pgdata
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 5s
      timeout: 5s
      retries: 10
    entrypoint: ["docker-entrypoint.sh"]
    command:
      - postgres
      - --max_connections=8192
      - --wal_level=minimal
      - --max_wal_senders=0
      - --port=5432
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == manager-01
      resources:
        limits:
          cpus: "2"
          memory: 2G
        reservations:
          cpus: "0.5"
          memory: 512M
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 0

  # ----------------------------------------- Redis

  redis:
    image: redis:latest
    read_only: true
    working_dir: /data
    networks:
      - intranet
    volumes:
      - redis:/data
    command: >
      redis-server
        --tcp-backlog 8192
        --tcp-keepalive 30
        --timeout 0
        --dir /data
        --save 16 1
        --save 12 10
        --save 6 100
        --rdbcompression no
        --appendonly yes
        --appendfsync everysec
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 1s
      timeout: 3s
      retries: 5
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == manager-01
      resources:
        limits:
          cpus: "1"
          memory: 1G
        reservations:
          cpus: "0.25"
          memory: 256M
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 0

  # ----------------------------------------- N8N Editor

  editor:
    <<: *n8n-common
    volumes:
      - editor:/data
      - type: tmpfs
        target: /run
        tmpfs:
          size: 536870912
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 536870912
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres, *env-services, *env-editor, *env-web, *env-core, *env-tasks]
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == manager-01
      resources:
        limits:
          cpus: "4"
          memory: 4G
        reservations:
          cpus: "1"
          memory: 1G
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 0
      labels:
        traefik.enable: "true"
        traefik.http.routers.n8n-editor.rule: "Host(`n8n.seudominio.com.br`)"
        traefik.http.routers.n8n-editor.entrypoints: "web,websecure"
        traefik.http.routers.n8n-editor.tls: "true"
        traefik.http.routers.n8n-editor.tls.certresolver: "letsencrypt"
        traefik.http.services.n8n-editor.loadbalancer.server.port: "5678"

  # ----------------------------------------- N8N Webhook

  webhook:
    <<: *n8n-common
    command: webhook
    volumes:
      - webhook:/data
      - type: tmpfs
        target: /run
        tmpfs:
          size: 536870912
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 536870912
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres, *env-services, *env-web]
    deploy:
      replicas: 4
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      rollback_config:
        parallelism: 1
        order: start-first
      resources:
        limits:
          cpus: "4"
          memory: 4G
        reservations:
          cpus: "0.5"
          memory: 512M
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 0
      labels:
        traefik.enable: "true"
        traefik.http.routers.n8n-webhook.rule: "Host(`ws.seudominio.com.br`)"
        traefik.http.routers.n8n-webhook.entrypoints: "web,websecure"
        traefik.http.routers.n8n-webhook.tls: "true"
        traefik.http.routers.n8n-webhook.tls.certresolver: "letsencrypt"
        traefik.http.services.n8n-webhook.loadbalancer.server.port: "5678"

  # ----------------------------------------- N8N Worker

  worker:
    <<: *n8n-common
    command: worker
    volumes:
      - worker:/data
      - type: tmpfs
        target: /run
        tmpfs:
          size: 536870912
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 536870912
    environment:
      <<: [*env-global, *env-queue, *env-redis, *env-postgres, *env-services, *env-core, *env-worker, *env-tasks]
    deploy:
      replicas: 4
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      rollback_config:
        parallelism: 1
        order: start-first
      resources:
        limits:
          cpus: "4"
          memory: 4G
        reservations:
          cpus: "0.5"
          memory: 512M
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 0

  # ----------------------------------------- N8N Task Runner

  runner:
    image: n8nio/runners:2.11.3
    networks:
      - intranet
    volumes:
      - runner:/data
      - type: tmpfs
        target: /run
        tmpfs:
          size: 536870912
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 536870912
    environment:
      <<: [*env-global, *env-runner, *env-tasks]
    deploy:
      replicas: 4
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      rollback_config:
        parallelism: 1
        order: start-first
      resources:
        limits:
          cpus: "4"
          memory: 4G
        reservations:
          cpus: "0.5"
          memory: 512M
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 0

Pronto, seu N8N está completo dentro do padrão da versão 2 e com todas as possibilidades para fazer o deploy.

Precisa se perder para achar
lugares que não se acham,
se não todos saberiam onde fica.
Capitão Barbossa – Piratas do Caribe

Terminamos por hoje!

Patrick Brandão, patrickbrandao@gmail.com