This commit is contained in:
Sean Robinson 2026-06-15 21:43:10 -04:00
commit c33afaf8f0
21 changed files with 1690 additions and 0 deletions

52
docker/n8n/.env.example Normal file
View file

@ -0,0 +1,52 @@
# ===========================================================================
# n8n stack environment
# Domain target: https://n8n.ld50.xyz
# ===========================================================================
# n8n image tag (use 'stable' for production)
N8N_VERSION=stable
# Host port for n8n main UI/API
EXPOSE_N8N_PORT=5678
# Runtime
NODE_ENV=production
GENERIC_TIMEZONE=America/Chicago
N8N_LOG_LEVEL=info
# Public URL config
N8N_HOST=n8n.ld50.xyz
N8N_PROTOCOL=https
N8N_PORT=5678
N8N_EDITOR_BASE_URL=https://n8n.ld50.xyz
WEBHOOK_URL=https://n8n.ld50.xyz/
# Security: REQUIRED
# Use a long random value and keep it identical across main + workers.
N8N_ENCRYPTION_KEY=replace-with-a-long-random-string
# Queue mode (recommended for production)
EXECUTIONS_MODE=queue
N8N_WORKER_CONCURRENCY=10
QUEUE_HEALTH_CHECK_ACTIVE=true
# PostgreSQL
DB_HOST=n8n-db
DB_PORT=5432
DB_DATABASE=n8n
DB_USERNAME=n8n
DB_PASSWORD=change-me-to-a-strong-db-password
# Redis
QUEUE_BULL_REDIS_HOST=n8n-redis
QUEUE_BULL_REDIS_PORT=6379
REDIS_PASSWORD=change-me-to-a-strong-redis-password
# Optional hardening / noise reduction
N8N_DIAGNOSTICS_ENABLED=false
N8N_VERSION_NOTIFICATIONS_ENABLED=false
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
# Optional shared external Docker network (for SWAG)
# Keep compose network stanza commented out until needed.
NETWORKS_EXTERNAL_NAME=swag

8
docker/n8n/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Runtime/persistent data
n8n-db-data/
n8n-redis-data/
n8n-data/
n8n-files/
# Local secrets
.env

View file

@ -0,0 +1,160 @@
name: n8n
services:
# ===========================================================================
# n8n Postgres (required for reliable production deployments)
# ===========================================================================
n8n-db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_DATABASE}
volumes:
- ./n8n-db-data:/var/lib/postgresql/data
healthcheck:
test:
[
"CMD",
"pg_isready",
"-h",
"n8n-db",
"-U",
"${DB_USERNAME:-n8n}",
"-d",
"${DB_DATABASE:-n8n}",
]
interval: 5s
timeout: 3s
retries: 30
networks:
- n8n
# ===========================================================================
# n8n Redis (queue broker)
# ===========================================================================
n8n-redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- ./n8n-redis-data:/data
healthcheck:
test:
[
"CMD-SHELL",
"redis-cli -a ${REDIS_PASSWORD:-change-me} ping | grep -q PONG",
]
interval: 5s
timeout: 3s
retries: 30
networks:
- n8n
# ===========================================================================
# n8n main process (UI + API + scheduler)
# ===========================================================================
n8n-main:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION:-stable}
restart: unless-stopped
depends_on:
n8n-db:
condition: service_healthy
n8n-redis:
condition: service_healthy
ports:
- ${EXPOSE_N8N_PORT:-5678}:5678
environment:
NODE_ENV: ${NODE_ENV:-production}
TZ: ${GENERIC_TIMEZONE:-UTC}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-UTC}
N8N_LOG_LEVEL: ${N8N_LOG_LEVEL:-info}
N8N_DIAGNOSTICS_ENABLED: ${N8N_DIAGNOSTICS_ENABLED:-false}
N8N_VERSION_NOTIFICATIONS_ENABLED: ${N8N_VERSION_NOTIFICATIONS_ENABLED:-false}
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: ${N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS:-true}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
N8N_HOST: ${N8N_HOST:-n8n.ld50.xyz}
N8N_PROTOCOL: ${N8N_PROTOCOL:-https}
N8N_PORT: ${N8N_PORT:-5678}
N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL:-https://n8n.ld50.xyz}
WEBHOOK_URL: ${WEBHOOK_URL:-https://n8n.ld50.xyz/}
EXECUTIONS_MODE: ${EXECUTIONS_MODE:-queue}
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: ${DB_HOST:-n8n-db}
DB_POSTGRESDB_PORT: ${DB_PORT:-5432}
DB_POSTGRESDB_DATABASE: ${DB_DATABASE:-n8n}
DB_POSTGRESDB_USER: ${DB_USERNAME:-n8n}
DB_POSTGRESDB_PASSWORD: ${DB_PASSWORD}
QUEUE_BULL_REDIS_HOST: ${QUEUE_BULL_REDIS_HOST:-n8n-redis}
QUEUE_BULL_REDIS_PORT: ${QUEUE_BULL_REDIS_PORT:-6379}
QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD}
QUEUE_HEALTH_CHECK_ACTIVE: ${QUEUE_HEALTH_CHECK_ACTIVE:-true}
# OpenTelemetry — export traces to the otel-lgtm pipeline
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://lgtm:4318}
OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-http/protobuf}
OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-n8n-main}
OTEL_RESOURCE_ATTRIBUTES: ${OTEL_RESOURCE_ATTRIBUTES:-deployment.environment=production}
volumes:
- ./n8n-data:/home/node/.n8n
- ./n8n-files:/files
networks:
- n8n
# - external_network
- pipeline
# ===========================================================================
# n8n worker (executes queued jobs)
# Scale this service out with: docker compose up -d --scale n8n-worker=3
# ===========================================================================
n8n-worker:
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION:-stable}
restart: unless-stopped
command: worker --concurrency=${N8N_WORKER_CONCURRENCY:-10}
depends_on:
n8n-db:
condition: service_healthy
n8n-redis:
condition: service_healthy
environment:
NODE_ENV: ${NODE_ENV:-production}
TZ: ${GENERIC_TIMEZONE:-UTC}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE:-UTC}
N8N_LOG_LEVEL: ${N8N_LOG_LEVEL:-info}
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: ${N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS:-true}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
EXECUTIONS_MODE: ${EXECUTIONS_MODE:-queue}
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: ${DB_HOST:-n8n-db}
DB_POSTGRESDB_PORT: ${DB_PORT:-5432}
DB_POSTGRESDB_DATABASE: ${DB_DATABASE:-n8n}
DB_POSTGRESDB_USER: ${DB_USERNAME:-n8n}
DB_POSTGRESDB_PASSWORD: ${DB_PASSWORD}
QUEUE_BULL_REDIS_HOST: ${QUEUE_BULL_REDIS_HOST:-n8n-redis}
QUEUE_BULL_REDIS_PORT: ${QUEUE_BULL_REDIS_PORT:-6379}
QUEUE_BULL_REDIS_PASSWORD: ${REDIS_PASSWORD}
QUEUE_HEALTH_CHECK_ACTIVE: ${QUEUE_HEALTH_CHECK_ACTIVE:-true}
# OpenTelemetry — export traces to the otel-lgtm pipeline
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://lgtm:4318}
OTEL_EXPORTER_OTLP_PROTOCOL: ${OTEL_EXPORTER_OTLP_PROTOCOL:-http/protobuf}
OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-n8n-worker}
OTEL_RESOURCE_ATTRIBUTES: ${OTEL_RESOURCE_ATTRIBUTES:-deployment.environment=production}
volumes:
- ./n8n-data:/home/node/.n8n
- ./n8n-files:/files
networks:
- n8n
- pipeline
networks:
n8n:
name: n8n
driver: bridge
# external_network:
# name: ${NETWORKS_EXTERNAL_NAME:-swag}
# external: true
pipeline:
name: pipeline
external: true

View file

@ -0,0 +1,40 @@
## -----------------------------------------------------------------------------
## SWAG proxy config for n8n
## Domain: n8n.ld50.xyz
## Upstream: n8n-main:5678 (shared Docker network: ${NETWORKS_EXTERNAL_NAME:-swag})
##
## Install:
## 1) Copy this file into SWAG: /config/nginx/proxy-confs/n8n.subdomain.conf
## 2) Ensure both stacks share the same external Docker network (e.g. `swag`).
## 3) In curated_compose/n8n/docker-compose.yaml, uncomment external_network.
## 4) Reload SWAG.
## -----------------------------------------------------------------------------
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name n8n.ld50.xyz;
include /config/nginx/ssl.conf;
# n8n imports/exports can be large
client_max_body_size 100M;
location / {
include /config/nginx/proxy.conf;
set $upstream_app n8n-main;
set $upstream_port 5678;
set $upstream_proto http;
proxy_pass $upstream_proto://$upstream_app:$upstream_port;
# Required for SSE / websocket-like upgrades used by parts of n8n
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}