<figure class="wp-block-image aligncenter"><img src="https://flowgenius.in/wp-content/uploads/2026/01/load-balancer-setup.png" alt="Step by Step Guide to solve load balancer setup" /> <figcaption style="text-align: center;">Step by Step Guide to solve load balancer setup</p>
<hr />
</figcaption></figure>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Who this is for:</strong> DevOps engineers or system administrators who need to run multiple n8n instances behind a reliable reverse‑proxy load balancer. <strong>We cover this in detail in the </strong><a href="https://flowgenius.in/n8n-performance-and-scaling-guide/">n8n Performance & Scaling Guide.</a></p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Quick Diagnosis</h2>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Problem:</strong> A single n8n instance becomes a bottleneck under concurrent workflows, causing time‑outs and a degraded UI.</p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Solution:</strong> Deploy a reverse‑proxy (Nginx or HAProxy) in front of <strong>≥ 2</strong> n8n containers/VMs, enable optional sticky sessions, and configure health checks. The steps below answer the featured‑snippet query “how to configure a load balancer for n8n”.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">1️⃣ Deploy ≥2 n8n instances (Docker, PM2, etc.)
2️⃣ Install Nginx or HAProxy on a dedicated host
3️⃣ Add upstream servers pointing to each n8n instance
4️⃣ Enable health checks & session persistence (optional)
5️⃣ Reload the balancer and verify with curl or the UI
</pre>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. Prerequisites & Environment Checklist</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Item</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Details</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><strong>n8n ≥ 1.0</strong> on separate hosts</td>
<td style="padding: 13px; border: 1px solid #ddd;">All instances must expose the same <code>WEBHOOK_URL</code> base (e.g., <code>https://n8n‑01.example.com</code>).</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Domain & DNS</td>
<td style="padding: 13px; border: 1px solid #ddd;">Pointing to the balancer IP – A single A record or CNAME provides a stable public endpoint.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">TLS certificate</td>
<td style="padding: 13px; border: 1px solid #ddd;">On the balancer – Terminating TLS off‑loads crypto work from n8n workers.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Firewall rules</td>
<td style="padding: 13px; border: 1px solid #ddd;">Allowing inbound 80/443 to balancer, outbound 5678 to each instance – Prevents accidental exposure of internal n8n ports.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Health‑check endpoint</td>
<td style="padding: 13px; border: 1px solid #ddd;">(<code>/healthz</code> or <code>/api/v1/ping</code>) reachable on each node – Required for automatic removal of failed nodes.</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd; font-style: italic;"><p><strong>EEFA note</strong> – Never expose the default n8n port (<code>5678</code>) directly to the internet. Use the balancer as the sole public entry point and lock down internal ports with firewall rules. If you encounter any <a href="/kubernetes-scaling-strategies">kubernetes scaling strategies </a>resolve them before continuing with the setup.</p></blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Nginx Load‑Balancing Configuration</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.1 Install Nginx (Ubuntu/Debian)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">sudo apt-get update
sudo apt-get install -y nginx
sudo systemctl enable --now nginx
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Installs and starts Nginx as a system service. If you encounter any <a href="/autoscaling-aws-ecs">autoscaling aws ecs </a>resolve them before continuing with the setup.</em></p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.2 Define the upstream pool</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Create <code>/etc/nginx/conf.d/n8n_upstream.conf</code> with the following snippet:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">upstream n8n_backend {
server 10.0.1.21:5678 max_fails=3 fail_timeout=30s;
server 10.0.1.22:5678 max_fails=3 fail_timeout=30s;
keepalive 32;
}
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Lists each n8n node; <code>keepalive</code> reduces TCP handshakes.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.3 HTTP → HTTPS redirect</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">server {
listen 80;
server_name n8n.example.com;
return 301 https://$host$request_uri;
}
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.4 TLS termination and proxy core</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Create the main HTTPS server block (first part – certificates and basic settings):</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">server {
listen 443 ssl http2;
server_name n8n.example.com;
ssl_certificate /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
}
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Handles TLS termination; replace paths with your cert files.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.5 Proxy the UI and API</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"> location / {
proxy_pass http://n8n_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.6 WebSocket support</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">n8n’s UI uses WebSockets; enable the required headers:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"> proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.7 Timeouts for long‑running workflows</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"> proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.8 Optional health‑check endpoint</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;"> location /healthz {
proxy_pass http://n8n_backend/healthz;
}
}
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.9 Sticky sessions (IP‑hash) – when needed</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">If the UI requires session persistence, replace the upstream definition with:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">upstream n8n_backend {
ip_hash;
server 10.0.1.21:5678;
server 10.0.1.22:5678;
}
</pre>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd; font-style: italic;"><p><strong>EEFA warning</strong> – <code>ip_hash</code> can skew traffic when many clients share a NAT IP. Prefer cookie‑based persistence if you need stickiness without uneven distribution.</p></blockquote>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.10 Test and reload</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">sudo nginx -t # syntax check
sudo systemctl reload nginx
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Verify the balancer responds:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">curl -I https://n8n.example.com
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">You should see <code>200 OK</code> and a <code>Server: nginx</code> header.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. HAProxy Load‑Balancing Configuration</h2>
<p>If you encounter any <a href="/horizontal-scaling-with-redis-queue">horizontal scaling with redis queue </a>resolve them before continuing with the setup.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.1 Install HAProxy (CentOS/RHEL)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">sudo yum install -y haproxy
sudo systemctl enable --now haproxy
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><em>Installs HAProxy and starts it as a service.</em></p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.2 Global and default settings</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Add the following to <code>/etc/haproxy/haproxy.cfg</code>:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">global
log /dev/log local0
daemon
maxconn 5000
tune.ssl.default-dh-param 2048
defaults
log global
mode http
option httplog
timeout connect 5s
timeout client 50s
timeout server 50s
timeout check 5s
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Sets process‑wide limits and basic HTTP defaults.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.3 Frontend – TLS termination and routing</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">frontend n8n_front
bind *:80
bind *:443 ssl crt /etc/letsencrypt/live/n8n.example.com/fullchain.pem alpn h2,http/1.1
http-request redirect scheme https unless { ssl_fc }
acl is_websocket hdr(Upgrade) -i WebSocket
use_backend n8n_ws if is_websocket
default_backend n8n_http
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Listens on both ports, redirects plain HTTP, and directs WebSocket traffic to a dedicated backend.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.4 Backend for HTTP API & UI</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">backend n8n_http
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server n8n01 10.0.1.21:5678 check inter 5s rise 2 fall 3
server n8n02 10.0.1.22:5678 check inter 5s rise 2 fall 3
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Performs health checks against <code>/healthz</code> and distributes requests evenly.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.5 Backend for WebSocket – cookie‑based stickiness</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">backend n8n_ws
balance roundrobin
cookie SERVERID insert indirect nocache
server n8n01 10.0.1.21:5678 check cookie n8n01
server n8n02 10.0.1.22:5678 check cookie n8n02
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Ensures a client stays on the same node for the duration of a WebSocket session.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">3.6 Reload HAProxy safely</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">sudo haproxy -c -f /etc/haproxy/haproxy.cfg # config test
sudo systemctl reload haproxy
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Confirm routing works:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto; margin-bottom: 2em;">curl -k https://n8n.example.com/api/v1/ping
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">A JSON response <code>{ "ping": "pong" }</code> indicates success.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Verification, Monitoring & Troubleshooting</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.1 Core health checks</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Check</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Command</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Expected Result</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Load‑balancer health</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>curl -I https://n8n.example.com/healthz</code></td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>200 OK</code> from **all** back‑ends</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Sticky session</td>
<td style="padding: 13px; border: 1px solid #ddd;">Open two browser tabs, start a workflow in each</td>
<td style="padding: 13px; border: 1px solid #ddd;">Both tabs stay on the same backend (inspect <code>X-Forwarded-For</code> in logs)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">WebSocket continuity</td>
<td style="padding: 13px; border: 1px solid #ddd;">Open n8n UI, watch network tab for <code>101 Switching Protocols</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">No “connection reset” errors</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Request distribution</td>
<td style="padding: 13px; border: 1px solid #ddd;">Nginx: <code>sudo nginx -s status</code> or HAProxy stats page (<code>/stats</code>)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Balanced request counts across nodes</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Error logs</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>tail -f /var/log/nginx/error.log</code> or <code>/var/log/haproxy.log</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">No <code>502 Bad Gateway</code> unless a node is truly down</td>
</tr>
</tbody>
</table>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.2 Common pitfalls & fixes</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Symptom</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Likely Cause</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">`502 Bad Gateway` after a node restart</td>
<td style="padding: 13px; border: 1px solid #ddd;">Health check still marks node **up** while service is down</td>
<td style="padding: 13px; border: 1px solid #ddd;">Reduce <code>inter</code>/<code>fall</code> values or enable <code>slow-start</code>.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">WebSocket disconnects</td>
<td style="padding: 13px; border: 1px solid #ddd;">Missing <code>Upgrade</code>/<code>Connection</code> headers</td>
<td style="padding: 13px; border: 1px solid #ddd;">Ensure the WebSocket location block (NGINX) or <code>n8n_ws</code> backend (HAProxy) is present.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Workflow webhook URLs point to internal IPs</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>WEBHOOK_URL</code> not set to public domain</td>
<td style="padding: 13px; border: 1px solid #ddd;">Set <code>WEBHOOK_URL=https://n8n.example.com</code> on every n8n instance.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Uneven traffic</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>ip_hash</code> used with many clients behind a NAT</td>
<td style="padding: 13px; border: 1px solid #ddd;">Switch to cookie‑based persistence or remove sticky config.</td>
</tr>
</tbody>
</table>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Scaling Beyond Two Instances</h2>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li><strong>Add more upstream servers</strong> – simply append additional <code>server X:X:5678</code> lines.</li>
<li><strong>Dynamic service discovery</strong> – integrate Consul, etcd, or Docker Swarm’s <code>server-template</code> (HAProxy) to auto‑populate the pool.</li>
<li><strong>Kubernetes</strong> – expose n8n via a <code>ClusterIP</code> Service and let an Ingress controller (NGINX Ingress) handle load‑balancing; the same upstream concepts apply.</li>
<li><strong>Rate limiting</strong> – protect downstream workers:<br />
<em>NGINX</em>: <code>limit_req zone=one burst=5 nodelay;</code><br />
<em>HAProxy</em>: <code>stick-table type ip size 200k expire 30s store gpc0</code></li>
</ol>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd; font-style: italic;"><p><strong>EEFA recommendation</strong> – When scaling past 5–10 nodes, off‑load TLS termination to an edge proxy (e.g., Cloudflare or AWS ALB) and keep the internal balancer HTTP‑only. This reduces CPU load on Nginx/HAProxy and simplifies certificate management.</p></blockquote>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Conclusion</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">Deploying a reverse‑proxy load balancer in front of multiple n8n instances eliminates the single‑point‑of‑failure bottleneck and provides:</p>
<ul style="margin-bottom: 2em; line-height: 1.9;">
<li><strong>High availability</strong> – health checks automatically remove unhealthy nodes.</li>
<li><strong>Scalability</strong> – add servers without changing client‑facing URLs.</li>
<li><strong>Performance</strong> – TLS termination and keep‑alive connections reduce latency.</li>
<li><strong>Reliability</strong> – sticky sessions (via cookies) keep WebSocket connections stable while preserving balanced traffic.</li>
</ul>
<p style="margin-bottom: 2em; line-height: 1.9;">Follow the step‑by‑step Nginx or HAProxy configurations above, verify health endpoints, and monitor request distribution. Your n8n deployment will now handle concurrent workflows with production‑grade resilience.</p>
Step by Step Guide to solve load balancer setup
Who this is for: DevOps engineers or system administrators who need to run multiple n8n instances behind a reliable reverse‑proxy load balancer. We cover this in detail in the n8n Performance & Scaling Guide.
Quick Diagnosis
Problem: A single n8n instance becomes a bottleneck under concurrent workflows, causing time‑outs and a degraded UI.
Solution: Deploy a reverse‑proxy (Nginx or HAProxy) in front of ≥ 2 n8n containers/VMs, enable optional sticky sessions, and configure health checks. The steps below answer the featured‑snippet query “how to configure a load balancer for n8n”.
1️⃣ Deploy ≥2 n8n instances (Docker, PM2, etc.)
2️⃣ Install Nginx or HAProxy on a dedicated host
3️⃣ Add upstream servers pointing to each n8n instance
4️⃣ Enable health checks & session persistence (optional)
5️⃣ Reload the balancer and verify with curl or the UI
1. Prerequisites & Environment Checklist
Item
Details
n8n ≥ 1.0 on separate hosts
All instances must expose the same WEBHOOK_URL base (e.g., https://n8n‑01.example.com).
Domain & DNS
Pointing to the balancer IP – A single A record or CNAME provides a stable public endpoint.
TLS certificate
On the balancer – Terminating TLS off‑loads crypto work from n8n workers.
Firewall rules
Allowing inbound 80/443 to balancer, outbound 5678 to each instance – Prevents accidental exposure of internal n8n ports.
Health‑check endpoint
(/healthz or /api/v1/ping) reachable on each node – Required for automatic removal of failed nodes.
EEFA note – Never expose the default n8n port (5678) directly to the internet. Use the balancer as the sole public entry point and lock down internal ports with firewall rules. If you encounter any kubernetes scaling strategies resolve them before continuing with the setup.
If the UI requires session persistence, replace the upstream definition with:
upstream n8n_backend {
ip_hash;
server 10.0.1.21:5678;
server 10.0.1.22:5678;
}
EEFA warning – ip_hash can skew traffic when many clients share a NAT IP. Prefer cookie‑based persistence if you need stickiness without uneven distribution.
Listens on both ports, redirects plain HTTP, and directs WebSocket traffic to a dedicated backend.
3.4 Backend for HTTP API & UI
backend n8n_http
balance roundrobin
option httpchk GET /healthz
http-check expect status 200
server n8n01 10.0.1.21:5678 check inter 5s rise 2 fall 3
server n8n02 10.0.1.22:5678 check inter 5s rise 2 fall 3
Performs health checks against /healthz and distributes requests evenly.
3.5 Backend for WebSocket – cookie‑based stickiness
A JSON response { "ping": "pong" } indicates success.
4. Verification, Monitoring & Troubleshooting
4.1 Core health checks
Check
Command
Expected Result
Load‑balancer health
curl -I https://n8n.example.com/healthz
200 OK from **all** back‑ends
Sticky session
Open two browser tabs, start a workflow in each
Both tabs stay on the same backend (inspect X-Forwarded-For in logs)
WebSocket continuity
Open n8n UI, watch network tab for 101 Switching Protocols
No “connection reset” errors
Request distribution
Nginx: sudo nginx -s status or HAProxy stats page (/stats)
Balanced request counts across nodes
Error logs
tail -f /var/log/nginx/error.log or /var/log/haproxy.log
No 502 Bad Gateway unless a node is truly down
4.2 Common pitfalls & fixes
Symptom
Likely Cause
Fix
`502 Bad Gateway` after a node restart
Health check still marks node **up** while service is down
Reduce inter/fall values or enable slow-start.
WebSocket disconnects
Missing Upgrade/Connection headers
Ensure the WebSocket location block (NGINX) or n8n_ws backend (HAProxy) is present.
Workflow webhook URLs point to internal IPs
WEBHOOK_URL not set to public domain
Set WEBHOOK_URL=https://n8n.example.com on every n8n instance.
Uneven traffic
ip_hash used with many clients behind a NAT
Switch to cookie‑based persistence or remove sticky config.
5. Scaling Beyond Two Instances
Add more upstream servers – simply append additional server X:X:5678 lines.
Dynamic service discovery – integrate Consul, etcd, or Docker Swarm’s server-template (HAProxy) to auto‑populate the pool.
Kubernetes – expose n8n via a ClusterIP Service and let an Ingress controller (NGINX Ingress) handle load‑balancing; the same upstream concepts apply.
Rate limiting – protect downstream workers: NGINX: limit_req zone=one burst=5 nodelay; HAProxy: stick-table type ip size 200k expire 30s store gpc0
EEFA recommendation – When scaling past 5–10 nodes, off‑load TLS termination to an edge proxy (e.g., Cloudflare or AWS ALB) and keep the internal balancer HTTP‑only. This reduces CPU load on Nginx/HAProxy and simplifies certificate management.
Conclusion
Deploying a reverse‑proxy load balancer in front of multiple n8n instances eliminates the single‑point‑of‑failure bottleneck and provides:
High availability – health checks automatically remove unhealthy nodes.
Scalability – add servers without changing client‑facing URLs.
Performance – TLS termination and keep‑alive connections reduce latency.
Follow the step‑by‑step Nginx or HAProxy configurations above, verify health endpoints, and monitor request distribution. Your n8n deployment will now handle concurrent workflows with production‑grade resilience.