Instalando N8N no MacOS

Saudações.

Vou ensinar como rodar o N8N direto no MacOS, sem Docker, sem container, sem dependências externas na Internet.

Antigamente a Nodemation (n8n) criava o aplicativo do N8N para desktop mas infelizmente descontinuaram o método, passando a operar somente em Docker e com foco em nuvem.

Nesse link você encontra como realizar o processo de compilação a partir dos fontes para construção da imagem OCI (Docker) do zero:

E nesse link você aprende sobre os modos de execução do N8N:

Aqui vou focar na execução baseada nos fontes para personalizações e tunings profundos.

Pré-requisitos:

  • Computador Apple Silicon: Mac Mini ou notebook Apple com MacOS;
  • Acesso à Internet durante os procedimentos.

1 – Preparação

Vou isolar a execução dentro da estrutura do brew.

Abra o Terminal do MacOS:

  • Aplicativos => Utilitários => Terminal;

Execute os procedimentos de instalação do brew em:

Após instalar o brew, feche o terminal e abra-o novamente.

Se preferir, alterne o shell padrão para o bash em vez do csh:

CSH
chsh -s /bin/bash;

Requer senha para alterar o shell, após alterar feche o Terminal e abra novamente.

Personalizando formato do shell:

BASH (MacOS)
# Banco e cinza
#export PS1='\[\033[0;99m\][\[\033[0;90m\]\u\[\033[0;99m\]@\[\033[0;99m\]\h\[\033[0;99m\]] \[\033[1;38m\]\w\[\033[0;99m\] \$\[\033[0m\] ';

# Verde e Amarelo
export PS1='\[\033[0;99m\][\[\033[0;92m\]\u\[\033[0;99m\]@\[\033[0;93m\]\h\[\033[0;99m\]] \[\033[1;38m\]\w\[\033[0;99m\] \$\[\033[0m\] ';

# Colocar personalizacao sempre que iniciar um novo shell:
egrep -q PS1 ~/.bash_profile 2>/dev/null || \
    /bin/echo "export PS1='$PS1';" >> ~/.bash_profile;

Feche o Terminal e abra novamente!

Atualizando brew antes de continuar:

BASH (MacOS)
# Atualizar brew:
brew update;
brew upgrade;

Garantir os binários do brew no caminho de busca dos programas no Terminal:

BASH (MacOS)
# Colocar homebrew no PATH
egrep -q '/opt/homebrew/bin' ~/.bash_profile 2>/dev/null || \
    /bin/echo "export PATH='/opt/homebrew/bin:$PATH';" >> ~/.bash_profile;

Feche o Terminal e abra novamente!

A pasta padrão do ecosistema brew é em: /opt/homebrew

Instalando programas necessários no brew:

BASH (MacOS)
# Uv
brew install uv;

# Git
brew install git;

# Python (necessário para algumas dependências, v3.14)
brew install python3;

# Jq - interpretador JSON
brew install jq;

#- Node.js - atual: 25
#- brew install node;
# Node.js - versao 24 requerida pelo n8n
brew install node@24;

# Instalar Corepack (ignore erros)
brew install corepack;
brew unlink corepack;
brew link --overwrite corepack;

2 – Compilar N8N do zero

O N8N é escrito em TypeScript (NodeJS), logo vamos precisar da pilha TS para preparar os fontes para execução.

2.1 – Baixando código fonte do N8N

Observem bem a versão do N8N que você irá usar. Link para conferir versões disponíveis:

BASH (MacOS)
# Diretorio para rodar o programa: /opt/homebrew/n8n-current
mkdir -p /opt/homebrew/n8n-current;

# Entrar no diretorio:
cd /opt/homebrew/n8n-current;

# Baixar do git a versao desejada:
N8N_VERSION="1.121.2";
git clone \
    --branch release/$N8N_VERSION \
    --single-branch https://github.com/n8n-io/n8n.git . || echo "Falhou no git";

Crie a chave de criptografia do N8N, usada para proteger canais e credenciais internas:

BASH (MacOS)
# Config minima estatica:
# tulipa ou md5(tulipa) = 52169089a52705298a67f2f8d9895c76
mkdir -p ~/.n8n;
(
    echo '{';
    echo '    "encryptionKey": "tulipa"';
    echo '}';
) > ~/.n8n/config;

# Permissoes fechadas:
chmod 600 /Users/patrickbrandao/.n8n/config;

2.2 – Preparando ambiente do projeto

Ajustes necessários para sincronizar as versões corretas do node e npm.

BASH (MacOS)
# Entrar no diretorio dos fontes:
cd /opt/homebrew/n8n-current;


# Ativar corepack:
corepack enable;

# Selecionar versao do pnpm ~ 10.18.3 (muda a cada update do n8n):
corepack prepare pnpm@10.23.0 --activate;


# Garantir versao 24 do node como padrão do sistema:
brew unlink node               2>/dev/null;
brew link node@24              2>/dev/null;
brew link --overwrite node@24  2>/dev/null;
    # Conferindo node 24:
    # Conferindo versões dos programas:
    node --version;
        # v24.11.1
    # Conferindo caminho dos binarios:
    which node;
        # /opt/homebrew/bin/node
    # Conferindo binário real do comando 'node':
    readlink -f /opt/homebrew/bin/node;
        # /opt/homebrew/Cellar/node@24/24.11.1/bin/node

# Retirar node 25 instalado pelo corepack
brew uninstall --ignore-dependencies node@25 2>/dev/null;

# Apontar node do corepack para a versao 24
mkdir -p /opt/homebrew/opt/node/bin;
ln -sf \
    /opt/homebrew/Cellar/node@24/24.11.1/bin/node \
    /opt/homebrew/opt/node/bin/node;

# Colocar corepack no projeto:
corepack up;

2.3 – Compilando

Essa é a parte demorada, onde tod

BASH (MacOS)
# Entrar no diretorio dos fontes:
cd /opt/homebrew/n8n-current;

# Ajuste de permissoes:
chmod -R u+rwX /opt/homebrew/n8n-current;

# Instalar dependencias infinitas, essa é a parte que DEMORA MUITO ~5min:
pnpm --loglevel verbose install;
    # Vai terminar com a mensagem na ultima linha:
    # "Done in 43s using pnpm v10.18.3"

# (coloque aqui sua etapa de personalizacao dos fontes)
# ...

# Construindo projeto unificado, DEMORA MUITO ~15min:
pnpm --loglevel verbose build;
pnpm --loglevel verbose build:n8n;

Após esse processo será produzida a pasta:

  • ./compiled: /opt/homebrew/n8n-current/compiled

Esse é o diretório que deve ser exportado para adição em container Docker e enviado para produção.

2.4 – Exportando projeto compilado

Vamos usar a pasta “compiled” para instalar o n8n no local definitivo em /opt/homebrew/n8n-app

BASH (MacOS)
# Criar diretorio final:
mkdir -p /opt/homebrew/n8n-app;

# Entrar no diretorio final:
cd /opt/homebrew/n8n-app;

# Copiar projeto compilado para pasta atual:
rsync -ravp /opt/homebrew/n8n-current/compiled/ /opt/homebrew/n8n-app/;

# Rodando versão compilada para teste:
# /opt/homebrew/n8n-app/bin/n8n start;
# CONTROL+C para interromper o teste.

2.5 – Compilando Task-Runner JavaScript

Nota: Etapa é opcional, faça somente se você precisar do Task-Runner para o MacOS.

BASH (MacOS)
# Entrar no diretorio dos fontes:
cd /opt/homebrew/n8n-current;

# Instalar task-runner Javascript
mkdir -p /opt/homebrew/tmp/runners;
rsync -ravp dist/task-runner-javascript /opt/homebrew/tmp/runners/;

# Compilar Task-Runners
cd /opt/homebrew/tmp/runners/task-runner-javascript;
corepack enable pnpm;

# Ajustar package.json
node -e "const pkg = require('./package.json'); \
    Object.keys(pkg.dependencies || {}).forEach(k => { \
        const val = pkg.dependencies[k]; \
        if (val === 'catalog:' || val.startsWith('catalog:') || val.startsWith('workspace:')) \
            delete pkg.dependencies[k]; \
    }); \
    Object.keys(pkg.devDependencies || {}).forEach(k => { \
        const val = pkg.devDependencies[k]; \
        if (val === 'catalog:' || val.startsWith('catalog:') || val.startsWith('workspace:')) \
            delete pkg.devDependencies[k]; \
    }); \
    delete pkg.devDependencies; \
    require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));";

# Instalar pacoate 'moment'
rm -f node_modules/.modules.yaml;
pnpm add moment@2.30.1 --prod --no-lockfile;

# Caminho do Task-Runner Javascript:
ls -lah /opt/homebrew/tmp/runners/task-runner-javascript/dist/start.js;

# Colocar Task-Runner junto com o N8N pronto
rsync -ravp /opt/homebrew/tmp/runners /opt/homebrew/n8n-app/;

# Remover diretorio temporario do runners
rm -rf /opt/homebrew/tmp/runners;

2.6 – Compilando Task-Runner Launcher

Nota: Etapa é opcional, faça somente se você precisar do Task-Runner para o MacOS.

O Task-Runner Launcher é responsável pelo gerenciamento da execução dos Task-Runners e pela comunicação com o processo worker do N8N.

BASH (MacOS)
# Pasta temporaria para compilacao
# - Remover e recriar pasta temporaria
rm -rf /opt/homebrew/tmp;
mkdir -p /opt/homebrew/tmp;
cd /opt/homebrew/tmp;

# Instalar Go
brew install go;

# Converir versao do Go
go version;
    # go version go1.25.4 darwin/arm64


# Obter codigo-fonte do Task-Runner-Launcher
git clone https://github.com/n8n-io/task-runner-launcher.git;
cd /opt/homebrew/tmp/task-runner-launcher;

# Compilar (modo simples)
#- go build -o task-runner-launcher ./cmd/launcher;

# Compilar com otimizações
GOOS=darwin GOARCH=arm64 go \
    build -ldflags="-s -w" \
    -o task-runner-launcher ./cmd/launcher;

# Conferindo tipo do binario compilado:
file ./task-runner-launcher;
    # ./task-runner-launcher: Mach-O 64-bit executable arm64

# Conferindo binario pronto (o erro abaixo significa que funcionou):
./task-runner-launcher --version;
    # 2025/11/25 01:21:20 ERROR Failed to load config:
    #    AuthToken: missing required value: N8N_RUNNERS_AUTH_TOKEN

# Distribuir binario TRL para estrutura do brew:
mkdir -p /opt/homebrew/n8n-app/runners;
rm -rf /opt/homebrew/n8n-app/runners/task-runner-launcher;
cp -av ./task-runner-launcher /opt/homebrew/n8n-app/runners/task-runner-launcher;

# Criar link simbolico no caminho oficial
ln -sf \
    /opt/homebrew/n8n-app/runners/task-runner-launcher \
    /opt/homebrew/bin/task-runner-launcher;

# Limpar diretorio usado na compilacao:
cd /opt/homebrew;
rm -rf /opt/homebrew/tmp;

3 – Preparativos para o modo fila

Para rodar o N8N em modo fila vamos precisar do Redis e do PostgreSQL no nosso MacOS.

3.1 – PostgreApp

A melhor forma de provisionar um serviço de banco de dados no MacOS é com o PGAPP:

Procedimentos:

  • Garanta a existência de um serviço PostgreSQL 18
  • Garanta que o serviço está marcado para iniciar automaticamente;
  • Garanta que o PostgresApp está marcado para iniciar automaticamente no MacOS.

Tela do PGAPP:

O próximo passo é criar o banco de dados do N8N no serviço PostgreSQL 18. Conecte-se no banco de dados postgres com usuário postgres e execute:

psql -U postgres
-- Criar usuario exclusivo para o n8n chamado "n8n_local_user":
CREATE USER n8n_local_user
    WITH PASSWORD 'n8nsql2025'
    CREATEDB
    LOGIN;

-- criar db e usuario do n8n chamado "n8n_local_db":
CREATE DATABASE n8n_local_db
    WITH 
    OWNER = n8n_local_user
    ENCODING = 'UTF8'
    TABLESPACE = pg_default
    IS_TEMPLATE = False
    CONNECTION LIMIT = -1;

-- Conectar no banco "n8n_local_db":
\c n8n_local_db;

-- Conceder todos os privilégios ao usuario "n8n_local_user" no banco "n8n_local_db"
GRANT ALL PRIVILEGES ON DATABASE n8n_local_db TO n8n_local_user;

-- Adicionar extensao (ignore alerta, se ja existir otimo):
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- Conceder privilégios no schema público
GRANT ALL ON SCHEMA public TO n8n_local_user;

-- Garantir privilégios em tabelas futuras
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES    TO n8n_local_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO n8n_local_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO n8n_local_user;

-- Verificar se o usuário foi criado
\du n8n_local_user;

-- Verificar se o banco foi criado
\l n8n_local_db;

-- Verificar privilégios no banco
\l+ n8n_local_db;

-- Sair
\q

Serviço PostgreSQL criado:

  • Endereço do servidor PostgreSQL: localhost (127.0.0.1 ou ::1) porta 5432
  • Usuário e senha de acesso: n8n_local_user senha n8nsql2025
  • Banco de dados chamado: n8n_local_db

Se desejar uma administração visual do banco de dados, instale o pgAdmin para MacOS:

3.2 – Redis Server

O Redis pode ser instalado usando o próprio brew:

BASH (MacOS)
# Entrar no diretorio do brew:
cd /opt/homebrew/;

# Remover redis nativo, se presente:
brew uninstall redis 2>/dev/null;

# Instalando Redis:
brew tap redis/redis;
brew install --cask redis;

# Conferindo configuração padrão:
cat /opt/homebrew/etc/redis.conf | egrep -v '^(#|$)';

# Garantir existencia do diretorio de dados:
mkdir -p /opt/homebrew/var/db/redis;

Nenhuma configuração especial é requerida no Redis.

Parar criar serviço do Redis no gestor de serviços do MacOS (Launcher) crie o arquivo:

  • ~/Library/LaunchAgents/com.redis-server.plist

Com o seguinte conteúdo:

~/Library/LaunchAgents/com.redis-server.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.redis-server</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/redis-server</string>
        <string>--bind '0.0.0.0 ::'</string>
        <string>--port 6379</string>
        <string>--set-proc-title no</string>
        <string>--tcp-backlog 8192</string>
        <string>--tcp-keepalive 30</string>
        <string>--timeout 0</string>
        <string>--save 900 1</string>
        <string>--save 300 10</string>
        <string>--save 60 10000</string>
        <string>--dir /opt/homebrew/var/db/redis/</string>
        <string>--rdbcompression no</string>
        <string>--rdbchecksum yes</string>
        <string>--dbfilename data.rdb</string>
        <string>--appendonly yes</string>
        <string>--appendfsync everysec</string>
        <string>--appendfilename data.aof</string>
    </array>

    <key>WorkingDirectory</key> <string>/opt/homebrew/var/db/redis</string>
    
    <key>EnvironmentVariables</key>
    <dict>
        <key>TZ</key><string>America/Sao_Paulo</string>
    </dict>

    <key>RunAtLoad</key> <true/>
    
    <key>KeepAlive</key> <true/>
    
    <key>StandardOutPath</key> <string>/tmp/redis-server-stdout.log</string>
    <key>StandardErrorPath</key> <string>/tmp/redis-server-error.log</string>
</dict>
</plist>

Registrar serviço e iniciar:

BASH (MacOS)
# Parar o serviço:
launchctl stop com.redis-server;

# Descarregar:
launchctl unload ~/Library/LaunchAgents/com.redis-server.plist;

# Limpar logs
echo > /tmp/redis-server-stdout.log;
echo > /tmp/redis-server-error.log;

# Carregar no cadastro de serviços:
launchctl load ~/Library/LaunchAgents/com.redis-server.plist;

# Parar o serviço (execute e aguarde uns 15s)
launchctl stop com.redis-server;

# Iniciar o serviço
launchctl start com.redis-server;

# Verificar status
launchctl list | grep redis;
    # rodando:  59073  0  com.redis-server
    # parado.:  -      0  com.redis-server

# Ver logs
tail -n 20 /tmp/redis-server-stdout.log;
tail -n 20 /tmp/redis-server-error.log;

# Acompanhar logs
tail -f /tmp/redis-server-stdout.log /tmp/redis-server-error.log;

Serviço Redis criado:

  • Endereço do servidor Redis: localhost (127.0.0.1 ou ::1) porta 6379
  • Usuário e senha de acesso: (sem senha)

Testando serviço Redis:

BASH (MacOS)
redis-cli -h localhost -p 6379;
    SET teste 123
    # OK

    GET teste
    "123"
    
    DEL teste
    (integer) 1
    
    GET teste
    (nil)

    exit

4 – Rodando N8N no modo fila no MacOS

Antes de continuar é necessário entender as variáveis de ambiente envolvidas.

4.1 – Variáveis de ambiente

Variáveis de ambiente que devem estar presentes em todos os serviços do N8N:

VariávelObjetivo
EXECUTIONS_MODEPadrão “regular”, alterar para queue
N8N_ENCRYPTION_KEYtulipa
TZAmerica/Sao_Paulo
GENERIC_TIMEZONEAmerica/Sao_Paulo
DB_TYPETipo de banco de dados, padrão “sqlite”, alterar para “postgresdb“.
DB_POSTGRESDB_HOSTEndereço IP/DNS do servidor PG, localhost
DB_POSTGRESDB_PORTPorta TCP do servidor PG, 5432
DB_POSTGRESDB_USERUsuário de autenticação, n8n_local_user
DB_POSTGRESDB_PASSWORDSenha de autenticação, n8nsql2025
DB_POSTGRESDB_DATABASENome do banco de dados, n8n_local_db
DB_POSTGRESDB_SCHEMANome do esquema, public
QUEUE_BULL_REDIS_HOSTEndereço do servidor Redis, localhost
QUEUE_BULL_REDIS_PORTPorta do servidor Redis, 6379
QUEUE_BULL_REDIS_DBNúmero do DB no Redis (0), mudar para 8
QUEUE_BULL_REDIS_PASSWORDSenha do Redis, deixar sem (padrão)
QUEUE_BULL_REDIS_DUALSTACKPermitir IPv4 e IPv6 na conexão, true
QUEUE_HEALTH_CHECK_ACTIVEVerificar serviço de filas, true

Personalize sse precisar.

Variáveis que não podem repetir:

VariávelObjetivo
N8N_PORTPadrão 5678, mas cada serviço precisará da sua porta pois todos estão rodando juntos.

Vou usar as seguintes portas:

  • Editor: porta 5780;
  • Webhook: porta 5690;
  • Worker: porta 5500, embora ele não deva abrir porta, por algum motivo ele insiste em abrir.

4.2 – Criando serviços do N8N no Launcher

Serviço: n8n-editor – Editor e execuções do editor (execuções manuais):

  • ~/Library/LaunchAgents/com.n8n-editor.plist
~/Library/LaunchAgents/com.n8n-editor.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.n8n-editor</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/node</string>
        <string>/opt/homebrew/n8n-app/bin/n8n</string>
        <string>start</string>
    </array>

    <key>WorkingDirectory</key> <string>/opt/homebrew</string>
    <key>RunAtLoad</key> <true/>
    <key>KeepAlive</key> <true/>
    <key>StandardOutPath</key> <string>/tmp/n8n-editor-stdout.log</string>
    <key>StandardErrorPath</key> <string>/tmp/n8n-editor-error.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>N8N_PORT</key>                   <string>5780</string>
        <key>N8N_PROXY_HOPS</key>             <string>1</string>
        <key>NODE_ENV</key>                   <string>production</string>
        <key>N8N_ENDPOINT_WEBHOOK</key>       <string>v1</string>
        <key>N8N_ENDPOINT_WEBHOOK_TEST</key>  <string>test</string>
        <key>N8N_ENDPOINT_WEBHOOK_WAIT</key>  <string>ws-wait</string>
        <key>N8N_ENDPOINT_MCP</key>           <string>mcp</string>
        <key>N8N_ENDPOINT_MCP_TEST</key>      <string>mcp-test</string>
        <key>N8N_EDITOR_BASE_URL</key>        <string>http://localhost:5780</string>
        <key>WEBHOOK_URL</key>                <string>http://localhost:5690</string>
        <key>N8N_BASE_URL</key>               <string>http://localhost:5780</string>
        <key>N8N_SECURE_COOKIE</key>          <string>false</string>

        <key>N8N_DISABLE_PRODUCTION_MAIN_PROCESS</key> <string>true</string>

        <key>N8N_DIAGNOSTICS_ENABLED</key>          <string>false</string>
        <key>N8N_BLOCK_ENV_ACCESS_IN_NODE</key>     <string>false</string>
        <key>N8N_GIT_NODE_DISABLE_BARE_REPOS</key>  <string>true</string>

        <key>N8N_USER_FOLDER</key> <string>/opt/homebrew/n8n-data</string>
        <key>N8N_CUSTOM_EXTENSIONS</key><string>/opt/homebrew/n8n-nodes</string>
        <key>N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE</key> <string>true</string>
        <key>N8N_COMMUNITY_PACKAGES_ENABLED</key> <string>true</string>
        <key>N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS</key> <string>true</string>

        <key>N8N_EMAIL_MODE</key>              <string>smtp</string>
        <key>N8N_SMTP_HOST</key>               <string>smtp.intranet.br</string>
        <key>N8N_SMTP_PORT</key>               <string>587</string>
        <key>N8N_SMTP_USER</key>               <string>username</string>
        <key>N8N_SMTP_PASS</key>               <string>pass123x</string>
        <key>N8N_SMTP_SENDER</key>             <string>root@intranet.br</string>
        <key>N8N_SMTP_SSL</key>                <string>true</string>

        <key>EXECUTIONS_MODE</key>             <string>queue</string>
        <key>N8N_ENCRYPTION_KEY</key>          <string>tulipa</string>
        <key>TZ</key>                          <string>America/Sao_Paulo</string>
        <key>GENERIC_TIMEZONE</key>            <string>America/Sao_Paulo</string>
        <key>DB_TYPE</key>                     <string>postgresdb</string>
        <key>DB_POSTGRESDB_HOST</key>          <string>localhost</string>
        <key>DB_POSTGRESDB_PORT</key>          <string>5432</string>
        <key>DB_POSTGRESDB_USER</key>          <string>n8n_local_user</string>
        <key>DB_POSTGRESDB_PASSWORD</key>      <string>n8nsql2025</string>
        <key>DB_POSTGRESDB_DATABASE</key>      <string>n8n_local_db</string>
        <key>DB_POSTGRESDB_SCHEMA</key>        <string>public</string>
        <key>QUEUE_BULL_REDIS_HOST</key>       <string>localhost</string>
        <key>QUEUE_BULL_REDIS_PORT</key>       <string>6379</string>
        <key>QUEUE_BULL_REDIS_DB</key>         <string>8</string>
        <key>QUEUE_BULL_REDIS_DUALSTACK</key>  <string>true</string>
        <key>QUEUE_HEALTH_CHECK_ACTIVE</key>   <string>true</string>

        <key>EXECUTIONS_TIMEOUT</key>                     <string>1800</string>
        <key>EXECUTIONS_TIMEOUT_MAX</key>                 <string>1800</string>
        <key>EXECUTIONS_DATA_PRUNE</key>                  <string>true</string>
        <key>EXECUTIONS_DATA_MAX_AGE</key>                <string>336</string>
        <key>EXECUTIONS_DATA_PRUNE_MAX_COUNT</key>        <string>2048</string>
        <key>EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL</key> <string>15</string>
        <key>EXECUTIONS_DATA_PRUNE_SOFT_DELETE_INTERVAL</key> <string>60</string>
        <key>EXECUTIONS_DATA_SAVE_ON_ERROR</key>          <string>all</string>
        <key>EXECUTIONS_DATA_SAVE_ON_SUCCESS</key>        <string>all</string>
        <key>EXECUTIONS_DATA_SAVE_ON_PROGRESS</key>       <string>true</string>
        <key>EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS</key> <string>true</string>
        <key>OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS</key>   <string>true</string>
    </dict>
</dict>
</plist>

Serviço: n8n-webhook – Servidor HTTP para escuta de Webhooks (chamadas HTTP)

  • Arquivo: ~/Library/LaunchAgents/com.n8n-webhook.plist
~/Library/LaunchAgents/com.n8n-webhook.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.n8n-webhook</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/node</string>
        <string>/opt/homebrew/n8n-app/bin/n8n</string>
        <string>webhook</string>
    </array>

    <key>WorkingDirectory</key> <string>/opt/homebrew</string>
    <key>RunAtLoad</key> <true/>
    <key>KeepAlive</key> <true/>
    <key>StandardOutPath</key> <string>/tmp/n8n-webhook-stdout.log</string>
    <key>StandardErrorPath</key> <string>/tmp/n8n-webhook-error.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>N8N_PORT</key>                   <string>5690</string>
        <key>N8N_PROXY_HOPS</key>             <string>1</string>
        <key>NODE_ENV</key>                   <string>production</string>
        <key>N8N_ENDPOINT_WEBHOOK</key>       <string>v1</string>
        <key>N8N_ENDPOINT_WEBHOOK_TEST</key>  <string>test</string>
        <key>N8N_ENDPOINT_WEBHOOK_WAIT</key>  <string>ws-wait</string>
        <key>N8N_ENDPOINT_MCP</key>           <string>mcp</string>
        <key>N8N_ENDPOINT_MCP_TEST</key>      <string>mcp-test</string>
        <key>N8N_EDITOR_BASE_URL</key>        <string>http://localhost:5780</string>
        <key>WEBHOOK_URL</key>                <string>http://localhost:5690</string>
        <key>N8N_BASE_URL</key>               <string>http://localhost:5780</string>

        <key>N8N_DIAGNOSTICS_ENABLED</key>          <string>false</string>
        <key>N8N_BLOCK_ENV_ACCESS_IN_NODE</key>     <string>false</string>
        <key>N8N_GIT_NODE_DISABLE_BARE_REPOS</key>  <string>true</string>

        <key>N8N_USER_FOLDER</key> <string>/opt/homebrew/n8n-data</string>
        <key>N8N_CUSTOM_EXTENSIONS</key><string>/opt/homebrew/n8n-nodes</string>
        <key>N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE</key> <string>true</string>
        <key>N8N_COMMUNITY_PACKAGES_ENABLED</key> <string>true</string>
        <key>N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS</key> <string>true</string>

        <key>EXECUTIONS_MODE</key>             <string>queue</string>
        <key>N8N_ENCRYPTION_KEY</key>          <string>tulipa</string>
        <key>TZ</key>                          <string>America/Sao_Paulo</string>
        <key>GENERIC_TIMEZONE</key>            <string>America/Sao_Paulo</string>
        <key>DB_TYPE</key>                     <string>postgresdb</string>
        <key>DB_POSTGRESDB_HOST</key>          <string>localhost</string>
        <key>DB_POSTGRESDB_PORT</key>          <string>5432</string>
        <key>DB_POSTGRESDB_USER</key>          <string>n8n_local_user</string>
        <key>DB_POSTGRESDB_PASSWORD</key>      <string>n8nsql2025</string>
        <key>DB_POSTGRESDB_DATABASE</key>      <string>n8n_local_db</string>
        <key>DB_POSTGRESDB_SCHEMA</key>        <string>public</string>
        <key>QUEUE_BULL_REDIS_HOST</key>       <string>localhost</string>
        <key>QUEUE_BULL_REDIS_PORT</key>       <string>6379</string>
        <key>QUEUE_BULL_REDIS_DB</key>         <string>8</string>
        <key>QUEUE_BULL_REDIS_DUALSTACK</key>  <string>true</string>
        <key>QUEUE_HEALTH_CHECK_ACTIVE</key>   <string>true</string>

        <key>EXECUTIONS_TIMEOUT</key>                     <string>1800</string>
        <key>EXECUTIONS_TIMEOUT_MAX</key>                 <string>1800</string>
        <key>EXECUTIONS_DATA_PRUNE</key>                  <string>true</string>
        <key>EXECUTIONS_DATA_MAX_AGE</key>                <string>336</string>
        <key>EXECUTIONS_DATA_PRUNE_MAX_COUNT</key>        <string>2048</string>
        <key>EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL</key> <string>15</string>
        <key>EXECUTIONS_DATA_PRUNE_SOFT_DELETE_INTERVAL</key> <string>60</string>
        <key>EXECUTIONS_DATA_SAVE_ON_ERROR</key>          <string>all</string>
        <key>EXECUTIONS_DATA_SAVE_ON_SUCCESS</key>        <string>all</string>
        <key>EXECUTIONS_DATA_SAVE_ON_PROGRESS</key>       <string>true</string>
        <key>EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS</key> <string>true</string>
        <key>OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS</key>   <string>true</string>
    </dict>
</dict>
</plist>

Serviço: n8n-worker – Processo responsável pela execução dos Workflows

  • Arquivo: ~/Library/LaunchAgents/com.n8n-worker.plist
~/Library/LaunchAgents/com.n8n-worker.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.n8n-worker</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/node</string>
        <string>/opt/homebrew/n8n-app/bin/n8n</string>
        <string>worker</string>
        <string>--concurrency=32</string>
    </array>

    <key>WorkingDirectory</key> <string>/opt/homebrew</string>
    <key>RunAtLoad</key> <true/>
    <key>KeepAlive</key> <true/>
    <key>StandardOutPath</key> <string>/tmp/n8n-worker-stdout.log</string>
    <key>StandardErrorPath</key> <string>/tmp/n8n-worker-error.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>N8N_PORT</key>                   <string>5500</string>
        <key>NODE_ENV</key>                   <string>production</string>
        <key>N8N_BASE_URL</key>               <string>http://localhost:5780</string>

        <key>N8N_DIAGNOSTICS_ENABLED</key>          <string>false</string>
        <key>N8N_BLOCK_ENV_ACCESS_IN_NODE</key>     <string>false</string>
        <key>N8N_GIT_NODE_DISABLE_BARE_REPOS</key>  <string>true</string>

        <key>N8N_USER_FOLDER</key> <string>/opt/homebrew/n8n-data</string>
        <key>N8N_CUSTOM_EXTENSIONS</key><string>/opt/homebrew/n8n-nodes</string>
        <key>N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE</key> <string>true</string>
        <key>N8N_COMMUNITY_PACKAGES_ENABLED</key> <string>true</string>
        <key>N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS</key> <string>true</string>

        <key>EXECUTIONS_MODE</key>             <string>queue</string>
        <key>N8N_ENCRYPTION_KEY</key>          <string>tulipa</string>
        <key>TZ</key>                          <string>America/Sao_Paulo</string>
        <key>GENERIC_TIMEZONE</key>            <string>America/Sao_Paulo</string>
        <key>DB_TYPE</key>                     <string>postgresdb</string>
        <key>DB_POSTGRESDB_HOST</key>          <string>localhost</string>
        <key>DB_POSTGRESDB_PORT</key>          <string>5432</string>
        <key>DB_POSTGRESDB_USER</key>          <string>n8n_local_user</string>
        <key>DB_POSTGRESDB_PASSWORD</key>      <string>n8nsql2025</string>
        <key>DB_POSTGRESDB_DATABASE</key>      <string>n8n_local_db</string>
        <key>DB_POSTGRESDB_SCHEMA</key>        <string>public</string>
        <key>QUEUE_BULL_REDIS_HOST</key>       <string>localhost</string>
        <key>QUEUE_BULL_REDIS_PORT</key>       <string>6379</string>
        <key>QUEUE_BULL_REDIS_DB</key>         <string>8</string>
        <key>QUEUE_BULL_REDIS_DUALSTACK</key>  <string>true</string>
        <key>QUEUE_HEALTH_CHECK_ACTIVE</key>   <string>true</string>

        <key>EXECUTIONS_TIMEOUT</key>                     <string>1800</string>
        <key>EXECUTIONS_TIMEOUT_MAX</key>                 <string>1800</string>
        <key>EXECUTIONS_DATA_PRUNE</key>                  <string>true</string>
        <key>EXECUTIONS_DATA_MAX_AGE</key>                <string>336</string>
        <key>EXECUTIONS_DATA_PRUNE_MAX_COUNT</key>        <string>2048</string>
        <key>EXECUTIONS_DATA_PRUNE_HARD_DELETE_INTERVAL</key> <string>15</string>
        <key>EXECUTIONS_DATA_PRUNE_SOFT_DELETE_INTERVAL</key> <string>60</string>
        <key>EXECUTIONS_DATA_SAVE_ON_ERROR</key>          <string>all</string>
        <key>EXECUTIONS_DATA_SAVE_ON_SUCCESS</key>        <string>all</string>
        <key>EXECUTIONS_DATA_SAVE_ON_PROGRESS</key>       <string>true</string>
        <key>EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS</key> <string>true</string>
        <key>OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS</key>   <string>true</string>
    </dict>
</dict>
</plist>

Registrar e executar serviços:

BASH (MacOS)
# Criar pasta para community-nodes:
mkdir -p /opt/homebrew/n8n-nodes;

# Criar pasta de arquivos do processo:
mkdir -p /opt/homebrew/n8n-data;


#------------------------------------------- Parar
# Parar os serviços
launchctl  stop  com.n8n-editor;
launchctl  stop  com.n8n-webhook;
launchctl  stop  com.n8n-worker;

# Desregistrar (para atualizar)
launchctl  unload  ~/Library/LaunchAgents/com.n8n-editor.plist  2>/dev/null;
launchctl  unload  ~/Library/LaunchAgents/com.n8n-webhook.plist 2>/dev/null;
launchctl  unload  ~/Library/LaunchAgents/com.n8n-worker.plist  2>/dev/null;

# Limpar logs
echo -n > /tmp/n8n-editor-error.log;
echo -n > /tmp/n8n-editor-stdout.log;
echo -n > /tmp/n8n-webhook-error.log;
echo -n > /tmp/n8n-webhook-stdout.log;
echo -n > /tmp/n8n-worker-error.log;
echo -n > /tmp/n8n-worker-stdout.log;


#------------------------------------------- Iniciar
# Carregar no cadastro de serviços:
launchctl  load  ~/Library/LaunchAgents/com.n8n-editor.plist;
launchctl  load  ~/Library/LaunchAgents/com.n8n-webhook.plist;
launchctl  load  ~/Library/LaunchAgents/com.n8n-worker.plist;

# Iniciar os serviços
launchctl  start  com.n8n-editor;
launchctl  start  com.n8n-webhook;
launchctl  start  com.n8n-worker;


# Verificar status
launchctl list | grep n8n;
    # 26377   0       com.n8n-webhook
    # 26080   0       com.n8n-editor
    # 26077   1       com.n8n-worker


# Ver logs
tail -n 20 /tmp/n8n*;

# Acompanhar logs
tail -f /tmp/n8n*;

O editor está na porta 5780 – http://localhost:5780/
O servidor HTTP de API (webhook) está na porta 5090 – http://localhost:5690/

O N8N está pronto. Use a vontade!

5 – Executar N8N com Task-Runner

Avançando rumo a perfeição, vamos compilar o Task-Runner (TR) para que ele se responsabilize pela execução de código Javascript/TypeScript e Python fora do Worker, criando no TR um ambiente Sandbox seguro para execuções de códigos de origem duvidosa (usuário, IA).

Vantagens:

  • Maior resiliência na execução de código estrangeiro
  • Segregação de execução de código estrangeiro em CPUs e servidores diferentes;

Desvantagens:

  • Mais lento, workflows que acionam node Code ficam mais lentos;
  • Latência de rede entre worker e task-runner afeta o tempo total de execução do worker;

Se você não quiser usar o Task-Runner, rode vários workers e crie rotinas que reiniciam os workers periodicamente para evitar memory-leak (acumulo de memória).

Pre-requisitos:

  • Task-Runner-Launcher;
  • Task-Runner-Javascript;

O Task-Runner-Python é opcional.

Configurar runners:

  • /etc/n8n-task-runners.json
/etc/n8n-task-runners.json
{
	"task-runners": [
		{
			"runner-type": "javascript",
			"workdir": "/opt/homebrew/n8n-app/runners/task-runner-javascript",
			"command": "/opt/homebrew/opt/node/bin/node",
			"args": [
				"--disallow-code-generation-from-strings",
				"--disable-proto=delete",
				"/opt/homebrew/n8n-app/runners/task-runner-javascript/dist/start.js"
			],
			"health-check-server-port": "5681",
			"allowed-env": [
				"PATH",
				"GENERIC_TIMEZONE",
				"NODE_OPTIONS",
				"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT",
				"N8N_RUNNERS_TASK_TIMEOUT",
				"N8N_RUNNERS_MAX_CONCURRENCY",
				"N8N_SENTRY_DSN",
				"N8N_VERSION",
				"ENVIRONMENT",
				"DEPLOYMENT_NAME"
			],
			"env-overrides": {
				"NODE_FUNCTION_ALLOW_BUILTIN": "crypto",
				"NODE_FUNCTION_ALLOW_EXTERNAL": "moment",
				"N8N_RUNNERS_HEALTH_CHECK_SERVER_HOST": "0.0.0.0"
			}
		}
	]
}

Variáveis que devem ser configuradas nas instâncias (editor, worker e webhook):

VariávelValor
N8N_RUNNERS_ENABLEDtrue
N8N_RUNNERS_MODEexternal
N8N_RUNNERS_AUTH_TOKENtulipa
N8N_RUNNERS_MAX_CONCURRENCY32
N8N_RUNNERS_TASK_TIMEOUT300
N8N_RUNNERS_HEARTBEAT_INTERVAL30
N8N_RUNNERS_INSECURE_MODEfalse
N8N_RUNNERS_TASK_REQUEST_TIMEOUT20

Variáveis que devem ser configuradas no Task-Runner-Launcher:

VariávelValor
N8N_RUNNERS_AUTH_TOKENtulipa
N8N_RUNNERS_MAX_CONCURRENCY32
N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT5588
N8N_RUNNERS_TASK_BROKER_URIhttp://127.0.0.1:5679
N8N_RUNNERS_LAUNCHER_LOG_LEVELinfo
N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT15

Declare as seguintes variáveis de ambiente nos serviços editor, webhook e worker:

LaunchAgents ENVs (part)

        <key>N8N_RUNNERS_ENABLED</key><string>true</string>
        <key>N8N_RUNNERS_PATH</key><string>/runners</string>
        <key>N8N_RUNNERS_MODE</key><string>external</string>
        <key>N8N_RUNNERS_AUTH_TOKEN</key><string>tulipa</string>
        <key>N8N_RUNNERS_MAX_CONCURRENCY</key><string>32</string>
        <key>N8N_RUNNERS_TASK_TIMEOUT</key><string>300</string>
        <key>N8N_RUNNERS_HEARTBEAT_INTERVAL</key><string>30</string>
        <key>N8N_RUNNERS_INSECURE_MODE</key><string>false</string>
        <key>N8N_RUNNERS_TASK_REQUEST_TIMEOUT</key><string>20</string>

É preciso adicionar a variável N8N_RUNNERS_BROKER_PORT com cuidado para não conflitar as portas na loopback:

  • Editor: N8N_RUNNERS_BROKER_PORT deve mudar da porta padrão para a porta 5501. O editor abre a porta mesmo no modo fila, mudar para não conflitar com a porta 5679 que deve ser exclusiva do worker;
  • Webhook: não abre porta broker, não precisa declarar;
  • Worker: N8N_RUNNERS_BROKER_PORT na porta 5679 oficial.

Agora é necessário criar o serviço do Task-Runner-Launcher no Launcher do MacOS:

  • ~/Library/LaunchAgents/com.task-runner-launcher.plist
~/Library/LaunchAgents/com.task-runner-launcher.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.task-runner-launcher</string>
    
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/n8n-app/runners/task-runner-launcher</string>
        <string>javascript</string>
    </array>

    <key>WorkingDirectory</key> <string>/opt/homebrew/n8n-app/runners</string>
    <key>RunAtLoad</key> <true/>
    <key>KeepAlive</key> <true/>
    <key>StandardOutPath</key> <string>/tmp/task-runner-launcher-stdout.log</string>
    <key>StandardErrorPath</key> <string>/tmp/task-runner-launcher-error.log</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>N8N_RUNNERS_AUTH_TOKEN</key><string>tulipa</string>
        <key>N8N_RUNNERS_MAX_CONCURRENCY</key><string>32</string>
        <key>N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT</key><string>5588</string>
        <key>N8N_RUNNERS_TASK_BROKER_URI</key><string>http://127.0.0.1:5679</string>
        <key>N8N_RUNNERS_LAUNCHER_LOG_LEVEL</key><string>info</string>
        <key>N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT</key><string>15</string>
    </dict>
    </dict>
</plist>

Registrar e iniciar serviço TRL:

BASH (MacOS)
#------------------------------------------- Parar
# Parar os serviços
launchctl  stop  com.task-runner-launcher;

# Desregistrar (para atualizar)
launchctl unload ~/Library/LaunchAgents/com.task-runner-launcher.plist 2>/dev/null;

# Limpar logs
echo -n > /tmp/task-runner-launcher-stdout.log;
echo -n > /tmp/task-runner-launcher-error.log;

#------------------------------------------- Iniciar
# Carregar no cadastro de serviços:
launchctl  load  ~/Library/LaunchAgents/com.task-runner-launcher.plist;

# Iniciar os serviços
launchctl  start  com.task-runner-launcher;


# Verificar status
launchctl list | grep task-runner-launcher;
    # 26413   0       task-runner-launcher

# Ver logs
tail -n 20 /tmp/task-runner-launcher-*;

# Acompanhar logs
tail -f /tmp/task-runner-launcher-*;

Após adicionar e iniciar o task-runner-launcher, reinicie os serviços do N8N:

BASH (MacOS)
# Recarregar os serviços do N8N:
# - Parar
launchctl  stop    com.n8n-editor;
launchctl  stop    com.n8n-webhook;
launchctl  stop    com.n8n-worker;
# - Retirar registro
launchctl  unload  ~/Library/LaunchAgents/com.n8n-editor.plist  2>/dev/null;
launchctl  unload  ~/Library/LaunchAgents/com.n8n-webhook.plist 2>/dev/null;
launchctl  unload  ~/Library/LaunchAgents/com.n8n-worker.plist  2>/dev/null;

# - Aguardar desligamento
sleep 5;

# Carregar e iniciar automaticamente
launchctl  load    ~/Library/LaunchAgents/com.n8n-editor.plist  2>/dev/null;
launchctl  load    ~/Library/LaunchAgents/com.n8n-webhook.plist 2>/dev/null;
launchctl  load    ~/Library/LaunchAgents/com.n8n-worker.plist  2>/dev/null;

6 – Testes de execução e APIs internas

Testar para conferir se as APIs e serviços estão respondendo.

BASH (MacOS)
# Conferir PID nas portas abertas
lsof -i tcp:5780; # editor, porta de servidor http
lsof -i tcp:5690; # webhook, porta de servidor http
lsof -i tcp:5588; # launcher server - health check
lsof -i tcp:5678; # worker, porta padrao
lsof -i tcp:5679; # worker, porta do Task Broker

# Endpoints
# - Editor
curl http://localhost:5780/healthz;            # {"status":"ok"}
curl http://localhost:5780/healthz/readiness;  # {"status":"ok"}

# - Webhook
curl http://localhost:5690/;  # deve retornar 404/not-found/html

# - Worker - porta padrao
curl http://localhost:5678/healthz;            # {"status":"ok"}
curl http://localhost:5678/healthz/readiness;  # {"status":"ok"}

# - Worker - porta do Task Broker
curl http://localhost:5679/healthz;            # {"status":"ok"}

# - Task-Runner Launcher
curl -s -X POST \
    -H "Content-Type: application/json" \
    -d '{"token":"tulipa"}' \
    http://localhost:5679/runners/auth;
    #{
    # "data":
    # {
    # "token":"50dac5034e737597bd358e99e31073d049bc60d0d0422415b55df0d48260a5ed"
    # }
    #}

Paris so é Paris se você vai de vez em quando, quando você vai todo dia Paris perde o seu encanto” Autor: Ricardo Thome

Terminamos por hoje,
Patrick Brandão <patrickbrandao@gmail.com>