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:
- https://brew.sh/
- Arquivo install.sh de https://github.com/Homebrew/install
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:
chsh -s /bin/bash;
Requer senha para alterar o shell, após alterar feche o Terminal e abra novamente.
Personalizando formato do shell:
# 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:
# Atualizar brew:
brew update;
brew upgrade;
Garantir os binários do brew no caminho de busca dos programas no Terminal:
# 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:
# 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:
# 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:
# 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.
# 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
# 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
# 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.
# 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.
# 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:
- Site: https://postgresapp.com/
- Download: https://postgresapp.com/downloads.html
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:
-- 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:
# 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:
<?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:
# 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:
redis-cli -h localhost -p 6379;
SET teste 123
# OK
GET teste
"123"
DEL teste
(integer) 1
GET teste
(nil)
exit4 – 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ável | Objetivo |
| EXECUTIONS_MODE | Padrão “regular”, alterar para queue |
| N8N_ENCRYPTION_KEY | tulipa |
| TZ | America/Sao_Paulo |
| GENERIC_TIMEZONE | America/Sao_Paulo |
| DB_TYPE | Tipo de banco de dados, padrão “sqlite”, alterar para “postgresdb“. |
| DB_POSTGRESDB_HOST | Endereço IP/DNS do servidor PG, localhost |
| DB_POSTGRESDB_PORT | Porta TCP do servidor PG, 5432 |
| DB_POSTGRESDB_USER | Usuário de autenticação, n8n_local_user |
| DB_POSTGRESDB_PASSWORD | Senha de autenticação, n8nsql2025 |
| DB_POSTGRESDB_DATABASE | Nome do banco de dados, n8n_local_db |
| DB_POSTGRESDB_SCHEMA | Nome do esquema, public |
| QUEUE_BULL_REDIS_HOST | Endereço do servidor Redis, localhost |
| QUEUE_BULL_REDIS_PORT | Porta do servidor Redis, 6379 |
| QUEUE_BULL_REDIS_DB | Número do DB no Redis (0), mudar para 8 |
| QUEUE_BULL_REDIS_PASSWORD | Senha do Redis, deixar sem (padrão) |
| QUEUE_BULL_REDIS_DUALSTACK | Permitir IPv4 e IPv6 na conexão, true |
| QUEUE_HEALTH_CHECK_ACTIVE | Verificar serviço de filas, true |
Personalize sse precisar.
Variáveis que não podem repetir:
| Variável | Objetivo |
| N8N_PORT | Padrã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
<?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
<?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
<?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:
# 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
{
"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ável | Valor |
| N8N_RUNNERS_ENABLED | true |
| N8N_RUNNERS_MODE | external |
| N8N_RUNNERS_AUTH_TOKEN | tulipa |
| N8N_RUNNERS_MAX_CONCURRENCY | 32 |
| N8N_RUNNERS_TASK_TIMEOUT | 300 |
| N8N_RUNNERS_HEARTBEAT_INTERVAL | 30 |
| N8N_RUNNERS_INSECURE_MODE | false |
| N8N_RUNNERS_TASK_REQUEST_TIMEOUT | 20 |
Variáveis que devem ser configuradas no Task-Runner-Launcher:
| Variável | Valor |
| N8N_RUNNERS_AUTH_TOKEN | tulipa |
| N8N_RUNNERS_MAX_CONCURRENCY | 32 |
| N8N_RUNNERS_LAUNCHER_HEALTH_CHECK_PORT | 5588 |
| N8N_RUNNERS_TASK_BROKER_URI | http://127.0.0.1:5679 |
| N8N_RUNNERS_LAUNCHER_LOG_LEVEL | info |
| N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT | 15 |
Declare as seguintes variáveis de ambiente nos serviços editor, webhook e worker:
<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
<?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:
#------------------------------------------- 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:
# 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.
# 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>
