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.

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.

A estrutura do WordPress é organizada em três camadas principais:

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:

Bash
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:

Bash
#!/bin/bash

# Variaveis
    NAME=traefik-app;
    LOCAL=$NAME.intranet.br;
    EMAIL="root@intranet.br";
    [ -f /etc/email ] && EMAIL=$(head -1 /etc/email);

# Imagem
    IMAGE=traefik:latest;
    docker pull $IMAGE;

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

# Renovar/rodar:
  docker rm -f $NAME 2>/dev/null;
  docker run \
    -d --restart=always \
    --name $NAME -h $LOCAL \
    --tmpfs /run:rw,noexec,nosuid,size=2m \
    --tmpfs /tmp:rw,noexec,nosuid,size=2m \
    --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 $DATADIR/letsencrypt:/etc/letsencrypt \
    -v $DATADIR/config:/etc/traefik \
    -v $DATADIR/logs:/logs \
    \
    $IMAGE \
      \
      --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“:

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“:

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)

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 (4 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

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_MAX16
N8N_FORMDATA_FILE_SIZE_MAX200

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

Usados somente pelo editor (frontend e backend).

VariávelValor
OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERStrue
N8N_EDITOR_BASE_URL(a explicar)
N8N_DISABLE_UIfalse
N8N_AI_ENABLEDfalse
N8N_AI_PROVIDERopenai
N8N_AI_OPENAI_API_KEY(vazio)
N8N_AI_TIMEOUT_MAX3600000
N8N_AI_ALLOW_SENDING_PARAMETER_VALUEStrue
N8N_AI_PERSIST_BUILDER_SESSIONSfalse
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

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

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

3.9 – Opções exclusivas do task-runner (1 variável)

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_CONCURRENCY10
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 (1 variável)

Usados pelo worker e task-runner.

VariávelValor
N8N_RUNNERS_AUTH_TOKEN
Padrão vazio, especificar por segurança
tulipa

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

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 /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/.env-n8n-global

/root/n8n/.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/.env-n8n-queue

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

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

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

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

/root/n8n/.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/.env-n8n-services

/root/n8n/.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/.env-n8n-editor

/root/n8n/.env-n8n-editor
N8N_EDITOR_BASE_URL=https://n8n.seudominio.com.br/
N8N_VERSION_NOTIFICATIONS_ENABLED=false
N8N_VERSION_NOTIFICATIONS_WHATS_NEW_ENABLED=false
N8N_PUBLIC_API_SWAGGERUI_DISABLED=true

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

/root/n8n/.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/.env-n8n-core

/root/n8n/.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/.env-n8n-worker

/root/n8n/.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/.env-n8n-runner

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

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

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

6.5 – Executando o editor (main)

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

/root/n8n/run-21-editor.sh
# Nome de DNS para acesso HTTPs
    # - Importar da variavel N8N_HOST no arquivo .env
    . /root/n8n/.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/.env-n8n-global \
        --env-file /root/n8n/.env-n8n-queue \
        --env-file /root/n8n/.env-n8n-redis \
        --env-file /root/n8n/.env-n8n-postgres \
        --env-file /root/n8n/.env-n8n-services \
        \
        --env-file /root/n8n/.env-n8n-editor \
        --env-file /root/n8n/.env-n8n-web \
        --env-file /root/n8n/.env-n8n-core \
        --env-file /root/n8n/.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á extraido da variável WEBHOOK_URL.
Script no arquivo /root/n8n/run-22-webhook.sh

/root/n8n/run-22-webhook.sh
# Nome de DNS para acesso HTTPs
    # - Importar da variavel N8N_HOST no arquivo .env
    . /root/n8n/.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/.env-n8n-global \
        --env-file /root/n8n/.env-n8n-queue \
        --env-file /root/n8n/.env-n8n-redis \
        --env-file /root/n8n/.env-n8n-postgres \
        --env-file /root/n8n/.env-n8n-services \
        \
        --env-file /root/n8n/.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/run-23-worker.sh

/root/n8n/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/.env-n8n-global \
        --env-file /root/n8n/.env-n8n-queue \
        --env-file /root/n8n/.env-n8n-redis \
        --env-file /root/n8n/.env-n8n-postgres \
        --env-file /root/n8n/.env-n8n-services \
        \
        --env-file /root/n8n/.env-n8n-core \
        --env-file /root/n8n/.env-n8n-worker \
        --env-file /root/n8n/.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/run-24-runner.sh

/root/n8n/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/.env-n8n-global \
        \
        --env-file /root/n8n/.env-n8n-runner \
        --env-file /root/n8n/.env-n8n-tasks \
        \
        -v /storage/n8n-app/runner:/data \
        \
         n8nio/runners:2.11.3;

6.9 – Execução concluida

Agora todos os serviços estão rodando.

Exportando arquivos do tutorial em formato MarkDown.
Arquivo: /root/n8n/export-markdown.sh

/root/n8n/export-markdown.sh
# Exportar projeto em markdown
(
    echo '# N8N v2 Deploy';
    echo;
    echo '## Arquivos ENV';
    for envfile in /root/n8n/.env*; do
        echo "### $envfile";
        echo;
        echo '```env'; cat $envfile; echo '```';
        echo;
    done;
    echo;
    echo '## Scripts';
    for script in /root/n8n/run*; do
        echo "### $script";
        echo;
        echo '```bash'; cat $script; echo '```';
        echo;
    done;
    echo;
) > /root/n8n/STARTHERE.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 /root/n8n/run-51-setup-admin.sh

/root/n8n/run-51-setup-admin.sh
# Nome de DNS para acesso HTTPs
    # - Importar da variavel N8N_HOST no arquivo .env
    . /root/n8n/.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;

    curl -v \
        -X POST \
        -H 'Accept: application/json, text/plain, */*' \
        -H 'Content-Type: application/json' \
        -d "$JSON_DATA" \
        "https://$N8N_HOST/rest/owner/setup";

8 – Stack para Docker Compose standalone

Agora vamos empacotar nosso setup para simplificar a implementação usarndo Docker Compose em um servidor sem swarm, ou seja, o Docker em 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.yml

/root/n8n-deploy/docker-compose-traefik.yml
networks:
  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"

8.1 – Básico – Versão stack 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/.

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

/root/n8n/docker-compose.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-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-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-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/.env-n8n-global
      - /root/n8n/.env-n8n-runner
      - /root/n8n/.env-n8n-tasks
    volumes:
      - /storage/n8n-app/runner:/data

    restart: unless-stopped

x

x

x

x

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

volumes:
  n8n-pgsql-data:
  n8n-redis-data:
  n8n-editor-data:
  n8n-worker-data:
  n8n-webhook-data:
  n8n-runner-data:

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-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-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-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/.env-n8n-global
      - /root/n8n/.env-n8n-runner
      - /root/n8n/.env-n8n-tasks
    volumes:
      - /storage/n8n-app/runner:/data

    restart: unless-stopped

networks:
  network_public:
    external: true

Pronto, seu N8N está completo dentro do padrão da versão 2.

.

.

.

.

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