Who this is for: DevOps or platform engineers who run n8n in production and need a secure, scalable setup. We cover this in detail in the Production‑Grade n8n Architecture.
Quick Diagnosis
Problem – A single n8n runtime handles UI, API, workflow storage and heavy data processing. That mixes responsibilities, creates security exposure, and leads to scaling bottlenecks.
Solution – Split the control‑plane (API, UI, workflow storage) from the data‑plane (execution workers, payload handling). Deploy them as isolated services, lock down network policies, and set execution modes for horizontal scaling and hardened security.
In production it usually appears after a few weeks of steady traffic, not on day one.
Summary
If you encounter any production grade n8n architecture resolve them before continuing with the setup.
| Layer | Primary Role | Typical Deployment | Key Settings |
|---|---|---|---|
| Control‑Plane | Manage workflow definitions, UI, authentication, orchestration | Single container/service (e.g., n8n-web) |
EXECUTIONS_MODE=main, N8N_HOST, N8N_PORT |
| Data‑Plane | Run workflow executions, process payloads, call external APIs | One‑or‑many workers behind a queue (e.g., n8n-worker) |
EXECUTIONS_MODE=queue, EXECUTIONS_PROCESS=worker, QUEUE_BULL_REDIS_HOST |
Result: Isolated control traffic, independent scaling of heavy workloads, and a clear security boundary.
1. Why Separate Control‑Plane from Data‑Plane?
If you encounter any n8n architecture anti patterns resolve them before continuing with the setup.
| Concern | Drawbacks of a Single Plane | Benefits of Separation |
|---|---|---|
| Security | UI & execution share the same network namespace → credential leakage | Control‑plane behind a strict firewall; workers run in a sandboxed subnet |
| Scalability | CPU‑intensive executions block UI responsiveness | Workers autoscale horizontally without touching the UI service |
| Maintainability | UI updates affect the execution runtime (and vice‑versa) | Independent versioning; patch control‑plane without restarting workers |
| Observability | Mixed logs make troubleshooting noisy | Separate log streams simplify monitoring and alerting |
EEFA Note – Run workers in a non‑privileged container or VM and enforce least‑privilege IAM for external API credentials.
2. Architectural Blueprint
Overview Diagram (markdown‑compatible)
+-------------------+ +-------------------+
| n8n Control‑Plane| | n8n Data‑Plane |
| (Web UI & API) | | (Execution Workers)|
| - n8n-web | <----> | - n8n-worker-1 |
| - DB (Postgres) | Queue | - n8n-worker-2 |
| - Auth Service | (Redis) | - … |
+-------------------+ +-------------------+
| |
| HTTPS (public) | Internal TCP (queue)
v v
Internet Users External APIs / Webhooks
Core Components
| Component | Placement | Recommended Settings |
|---|---|---|
| n8n‑web (UI + API) | Control‑Plane | EXECUTIONS_MODE=main |
| n8n‑worker (execution) | Data‑Plane | EXECUTIONS_MODE=queue + EXECUTIONS_PROCESS=worker |
| PostgreSQL | Control‑Plane (or managed DB) | DB_TYPE=postgresdb |
| Redis (BullMQ) | Shared but network‑isolated | QUEUE_BULL_REDIS_HOST |
| Reverse Proxy (Traefik/NGINX) | Control‑Plane edge | TLS termination, IP allow‑list |
3. Deploying a Split Architecture with Docker‑Compose
Prerequisite – Docker Engine ≥ 20.10, Docker Compose ≥ 2.0.
3.1 Directory Layout
n8n/ ├─ docker-compose.yml ├─ .env.control # env for control‑plane ├─ .env.worker # env for data‑plane workers └─ redis/ └─ redis.conf
3.2 Control‑Plane Environment (.env.control)
# Core n8n settings N8N_HOST=0.0.0.0 N8N_PORT=5678 N8N_BASIC_AUTH_ACTIVE=true N8N_BASIC_AUTH_USER=admin N8N_BASIC_AUTH_PASSWORD=StrongP@ssw0rd # Execution mode – only orchestrates EXECUTIONS_MODE=main EXECUTIONS_PROCESS=main # Queue connection (read‑only) QUEUE_BULL_REDIS_HOST=redis QUEUE_BULL_REDIS_PORT=6379
3.3 Data‑Plane Environment (.env.worker)
# Workers never expose UI N8N_HOST=0.0.0.0 N8N_PORT=5679 # internal only # Execution mode – consumes jobs EXECUTIONS_MODE=queue EXECUTIONS_PROCESS=worker # Queue connection (read/write) QUEUE_BULL_REDIS_HOST=redis QUEUE_BULL_REDIS_PORT=6379 # OPTIONAL: limit CPU for sandboxing N8N_MAX_EXECUTION_TIMEOUT=3600
3.4 Docker‑Compose Service Definitions
Redis Service
redis:
image: redis:7-alpine
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
networks:
- n8n-net
PostgreSQL Service
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: n8nPass!
POSTGRES_DB: n8n
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- n8n-net
Control‑Plane Container (n8n-web)
n8n-web:
image: n8nio/n8n:latest
env_file:
- .env.control
ports:
- "5678:5678"
depends_on:
- db
- redis
networks:
- n8n-net
restart: unless-stopped
Data‑Plane Workers (n8n-worker)
n8n-worker:
image: n8nio/n8n:latest
env_file:
- .env.worker
depends_on:
- db
- redis
networks:
- n8n-net
deploy:
mode: replicated
replicas: 3 # scale horizontally
restart: unless-stopped
Compose Boilerplate
version: "3.8"
networks:
n8n-net:
driver: bridge
volumes:
pgdata:
The pieces above are deliberately kept separate so you can tweak one side without touching the other.
3.5 Bring the Stack Up
docker compose up -d docker compose ps # verify all services are running
EEFA Warning – Never expose the
n8n-workerport to the public internet. Keep it confined to the internal Docker network or block it with firewall rules. If you encounter any n8n multi tenant architecture resolve them before continuing with the setup.
4. Kubernetes‑Native Split (Production‑Grade)
4.1 Namespace Layout
apiVersion: v1 kind: Namespace metadata: name: n8n-control --- apiVersion: v1 kind: Namespace metadata: name: n8n-data
4.2 Control‑Plane Deployment (n8n-web)
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n-web
namespace: n8n-control
spec:
replicas: 1
selector:
matchLabels:
app: n8n-web
template:
metadata:
labels:
app: n8n-web
spec:
containers:
- name: n8n
image: n8nio/n8n:latest
envFrom:
- secretRef:
name: n8n-control-env
ports:
- containerPort: 5678
resources:
limits:
cpu: "500m"
memory: "512Mi"
Service exposing the UI
apiVersion: v1
kind: Service
metadata:
name: n8n-web
namespace: n8n-control
spec:
selector:
app: n8n-web
ports:
- protocol: TCP
port: 80
targetPort: 5678
type: ClusterIP
4.3 Data‑Plane Workers (n8n-worker)
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n-worker
namespace: n8n-data
spec:
replicas: 3 # HPA can adjust this
selector:
matchLabels:
app: n8n-worker
template:
metadata:
labels:
app: n8n-worker
spec:
containers:
- name: n8n
image: n8nio/n8n:latest
envFrom:
- secretRef:
name: n8n-worker-env
ports:
- containerPort: 5679
resources:
limits:
cpu: "1000m"
memory: "1Gi"
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
Service for the workers (internal queue)
apiVersion: v1
kind: Service
metadata:
name: n8n-queue
namespace: n8n-data
spec:
selector:
app: n8n-worker
ports:
- protocol: TCP
port: 5679
targetPort: 5679
type: ClusterIP
4.4 Shared Queue & Database (Redis & PostgreSQL)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-n8n
namespace: n8n-infra
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: n8n-control
- namespaceSelector:
matchLabels:
name: n8n-data
ports:
- protocol: TCP
port: 6379 # Redis
- protocol: TCP
port: 5432 # Postgres
EEFA Tip – Enforce Pod Security Standards (runAsUser ≥ 1000, drop all Linux capabilities) on the worker pods.
4.5 Autoscaling the Workers
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: n8n-worker-hpa
namespace: n8n-data
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: n8n-worker
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
5. Troubleshooting Checklist
| Symptom | Likely Cause | Diagnostic Steps | Fix |
|---|---|---|---|
| UI hangs | Workers saturate Redis → back‑pressure on API | kubectl logs n8n-web → look for “Queue size exceeds limit” |
Add more worker replicas or raise QUEUE_BULL_MAX_JOBS |
| Execution fails with ECONNREFUSED | Worker cannot reach Redis (NetworkPolicy) | kubectl exec -it <worker-pod> -- curl redis:6379 |
Update NetworkPolicy to allow traffic |
| Credential leak in logs | Worker runs as root, writes secrets to stdout | Inspect pod securityContext |
Set runAsNonRoot: true, mount secrets as files |
| High UI‑to‑worker latency | Cross‑zone traffic, no locality | Ping between pods, check RTT | Deploy workers in same zone or enable VPC peering |
| DB connection timeout | Control‑plane points to wrong DB host | Verify DB_POSTGRESDB_HOST env var |
Point to managed DB endpoint, enable TLS |
EEFA Reminder – Enable audit logging on PostgreSQL and Redis. In regulated environments, encrypt data‑in‑flight (
REDIS_TLS_ENABLED=true,POSTGRES_SSLMODE=require).
6. Security Hardening Checklist
- Network segmentation – Control‑plane in a public subnet, data‑plane in a private subnet. Use security groups or NetworkPolicies to block inbound traffic to workers.
- Least‑privilege IAM – Separate IAM roles for UI (read/write workflows) vs workers (execute only).
- Secret management – Store API keys in Vault, AWS Secrets Manager, or Kubernetes Secrets with sealed‑secrets. Never embed them in Docker env files.
- Runtime sandboxing – Enable
N8N_EXECUTIONS_SAND_BOXED=true(Docker sandbox) or run workers inside gVisor / Kata Containers for extra isolation. - TLS everywhere – Terminate TLS at the reverse proxy for the UI; use mutual TLS between control‑plane and workers when possible.
7. Performance Tuning – Scaling the Data‑Plane
| Parameter | Description | Suggested Starting Value |
|---|---|---|
EXECUTIONS_TIMEOUT |
Max seconds a workflow may run | 3600 (1 h) |
EXECUTIONS_MAX_TIMEOUT |
Hard ceiling for any workflow | 86400 (24 h) |
QUEUE_BULL_MAX_JOBS |
Max queued jobs per worker | 200 |
N8N_MAX_WORKFLOW_RUNS |
Parallel runs per worker pod | 10 |
N8N_WORKER_CONCURRENCY |
Number of Node.js worker threads | 4 (CPU cores) |
Tuning Steps
- Baseline – Run a load test (e.g.,
hey -n 5000 -c 50 http://n8n-web/api/v1/workflows) and capture queue length. - Add workers – Increase
replicasin the worker Deployment ordocker compose up --scale n8n-worker=5. Adding more workers is usually faster than trying to squeeze more CPU out of a single instance. - Raise queue limits – If you see “Queue full” errors, bump
QUEUE_BULL_MAX_JOBS. - Monitor – Pull Prometheus metrics (
/metrics) and watchn8n_queue_job_countandn8n_execution_duration_seconds.
Bottom Line
Decoupling the control‑plane (UI, API, orchestration) from the data‑plane (execution workers) gives you a secure, horizontally‑scalable, and maintainable n8n deployment. Follow the Docker‑Compose or Kubernetes recipes above, validate with the troubleshooting checklist, and iterate on the performance metrics to keep your automation platform robust in real‑world production.



