<figure class="wp-block-image aligncenter"><img src="https://flowgenius.in/wp-content/uploads/2026/01/multi-instance-sync.png" alt="Step by Step Guide to solve multi instance sync" /><figcaption style="text-align: center;">Step by Step Guide to solve multi instance sync</p>
<hr />
</figcaption></figure>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Who this is for:</strong> DevOps engineers or platform engineers who need to run n8n in a horizontally‑scaled Docker/Kubernetes environment with deterministic execution. <em>Keep workflow state consistent across scaled instances. <strong>We cover this in detail in the </strong><a href="https://flowgenius.in/n8n-performance-and-scaling-guide/">n8n Performance & Scaling Guide.</a></em></p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Sync Setup</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Goal</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Minimal Config</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Required Services</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Key Env Vars</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Consistent workflow execution across 3+ n8n nodes</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Use <strong>PostgreSQL</strong> for DB + <strong>Redis</strong> (or <strong>RabbitMQ</strong>) for queue</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">PostgreSQL 13+, Redis 6+ (or RabbitMQ 3.8+)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">DB_TYPE=postgresdb<br />
DB_POSTGRESDB_HOST=postgres<br />
EXECUTIONS_PROCESS=queue<br />
QUEUE_BULL_REDIS_HOST=redis</td>
</tr>
</tbody>
</table>
<ol style="margin-bottom: 2em; line-height: 1.9;">
<li>Deploy a shared PostgreSQL instance.</li>
<li>Deploy a Redis (or RabbitMQ) broker.</li>
<li>Apply the env‑vars above on <strong>every</strong> n8n container.</li>
<li>Restart all nodes – they now share workflow definitions and pull jobs from a single queue, guaranteeing state consistency.</li>
</ol>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Quick Diagnosis: What Problem Does This Page Solve?</h2>
<p><strong>If you encounter any </strong><a href="/database-optimization">database optimization </a><strong>resolve them before continuing with the setup.</strong></p>
<p style="margin-bottom: 2em; line-height: 1.9;">When you horizontally scale n8n (Docker Swarm, Kubernetes, or multiple VMs), each node keeps its own execution cache. Without coordination the same workflow can fire on several nodes simultaneously, causing duplicate actions, race conditions, and data drift.</p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Solution</strong> – Centralize workflow definitions, execution state, and queue handling using a shared PostgreSQL database and a distributed job broker (Redis or RabbitMQ). The guide below shows the exact configuration, minimal code snippets, and troubleshooting steps to achieve deterministic, conflict‑free execution across any number of n8n instances.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. Execution Model Overview</h2>
<h3 style="margin-bottom: 40px; line-height: 1.3;">1.1 Workflow Storage</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Mode</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Storage Backend</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Note</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Single‑instance</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">SQLite (local file)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Not shareable across containers</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Multi‑instance</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">PostgreSQL / MySQL / MariaDB</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Network‑accessible DB required</td>
</tr>
</tbody>
</table>
<h3 style="margin-bottom: 40px; line-height: 1.3;">1.2 Execution Queue</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Mode</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Queue Implementation</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Benefit</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Single‑instance</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">In‑process (synchronous)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Simple, but no deduplication</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Multi‑instance</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Bull (Redis) <strong>or</strong> RabbitMQ</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Guarantees exactly‑once processing</td>
</tr>
</tbody>
</table>
<h3 style="margin-bottom: 40px; line-height: 1.3;">1.3 Static Data & Credentials</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Storage</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Location</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Recommendation</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Local files (<code>/root/.n8n</code>)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Container FS</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>Do not</strong> use in scaled setups</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">DB‑backed tables (<code>staticData</code>)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Shared DB</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Default when <code>DB_TYPE</code> is external</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;">When <code>EXECUTIONS_PROCESS=queue</code> is set, n8n publishes every trigger event to the broker. Any node can pull the job, ensuring a single execution per trigger. If you encounter any <a href="/database-migration-strategies">database migration strategies </a>resolve them before continuing with the setup.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Choosing a Shared Persistence Layer</h2>
<h3 style="margin-bottom: 40px; line-height: 1.3;">2.1 Database Options</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">DB Engine</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Pros</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Cons</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>PostgreSQL</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Strong ACID, JSONB, <code>SELECT FOR UPDATE</code> locking</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Slightly heavier</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">MySQL / MariaDB</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Familiar to many DBAs</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Limited JSON querying (MySQL 5.7+)</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">SQLite (shared NFS)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Zero‑config</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">File‑locking issues under concurrency – <strong>not recommended</strong></td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA</strong> – PostgreSQL’s <code>FOR UPDATE SKIP LOCKED</code> prevents workers from stealing each other’s execution rows, eliminating duplicate runs.</p>
<h3 style="margin-bottom: 40px; line-height: 1.3;">2.2 Queue Broker Options</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Broker</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Typical Latency</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Scaling Ease</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Persistence</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;"><strong>Redis (Bull)</strong></td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">< 5 ms</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Horizontal scaling trivial</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">In‑memory, optional RDB/AOF</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">RabbitMQ</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">~ 10 ms</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Supports complex routing</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Durable queues</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Kafka</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">~ 1 ms (high‑throughput)</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Very large clusters</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Log‑based persistence – overkill for most n8n workloads</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA</strong> – Reuse an existing Redis cache if available; otherwise RabbitMQ gives built‑in dead‑letter handling for failed jobs. If you encounter any <a href="/cache-layer-implementation">cache layer implementation </a>resolve them before continuing with the setup.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. Configuring Queue Mode with Redis</h2>
<h3 style="margin-bottom: 40px; line-height: 1.3;">3.1 Docker‑Compose: Service Definitions</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>PostgreSQL service (5 lines)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">postgres:
image: postgres:13
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: secret
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Redis service (5 lines)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">redis:
image: redis:6-alpine
command: ["redis-server", "--appendonly", "yes"]
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>n8n service (5 lines – core env vars)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">n8n:
image: n8nio/n8n:latest
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- EXECUTIONS_PROCESS=queue
- QUEUE_BULL_REDIS_HOST=redis
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>n8n service – optional auth & ports (5 lines)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"> - N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=supersecret
ports:
- "5678:5678"
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA</strong> – <code>appendonly yes</code> in Redis ensures queued jobs survive node restarts.</p>
<h3 style="margin-bottom: 40px; line-height: 1.3;">3.2 Kubernetes Manifest – Deployment & Service</h3>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Deployment (first 5 lines)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
spec:
replicas: 3
selector:
matchLabels:
app: n8n
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Container spec with env (next 5 lines)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"> template:
metadata:
labels:
app: n8n
spec:
containers:
- name: n8n
image: n8nio/n8n:latest
env:
- name: DB_TYPE
value: "postgresdb"
- name: DB_POSTGRESDB_HOST
value: "postgres-svc"
- name: EXECUTIONS_PROCESS
value: "queue"
- name: QUEUE_BULL_REDIS_HOST
value: "redis-svc"
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Service (5 lines)</strong></p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">---
apiVersion: v1
kind: Service
metadata:
name: n8n-svc
spec:
selector:
app: n8n
ports:
- port: 80
targetPort: 5678
type: LoadBalancer
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA</strong> – Use a headless Service for Redis if you need stable DNS per pod. Add <code>podAntiAffinity</code> to avoid co‑locating all n8n pods on a single node.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Synchronizing Static Data, Credentials & Secrets</h2>
<h3 style="margin-bottom: 40px; line-height: 1.3;">4.1 Migration Checklist</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Steps</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">1</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Ensure <code>DB_TYPE</code> is <strong>not</strong> <code>sqlite</code>.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">2</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Export local credentials: <code>n8n export:credentials --output ./creds.json</code>.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">3</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Import them: <code>n8n import:credentials --input ./creds.json</code>.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">4</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Delete the local <code>.n8n</code> folder on each node (or mount it read‑only).</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">5</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Restart all n8n instances.</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA</strong> – Credentials are encrypted with <code>N8N_ENCRYPTION_KEY</code>. The same key <strong>must</strong> be set on every pod; otherwise imported credentials become unreadable.</p>
<h3 style="margin-bottom: 40px; line-height: 1.3;">4.2 Setting a Shared Encryption Key (4 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">export N8N_ENCRYPTION_KEY=$(openssl rand -base64 32)
docker run -e N8N_ENCRYPTION_KEY=$N8N_ENCRYPTION_KEY \
-e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_HOST=postgres \
-e EXECUTIONS_PROCESS=queue \
-e QUEUE_BULL_REDIS_HOST=redis \
n8nio/n8n
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Copy the printed key into the environment of <strong>all</strong> n8n instances.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Health Checks, Conflict Resolution & Idempotency</h2>
<h3 style="margin-bottom: 40px; line-height: 1.3;">5.1 Built‑in Health Endpoint (4 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">livenessProbe:
httpGet:
path: /healthz
port: 5678
initialDelaySeconds: 30
periodSeconds: 10
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Configure your load balancer to route traffic only to pods that report healthy.</p>
<h3 style="margin-bottom: 40px; line-height: 1.3;">5.2 Idempotent Trigger Patterns</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Technique</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Implementation</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Idempotent nodes</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Store a unique request ID in workflow static data (<code>Set</code> node) and skip processing if it already exists.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Webhook signature verification</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Validate HMAC header before any business logic.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Debounce logic</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Add a <code>Delay</code> node with <strong>`Wait Until`</strong> to coalesce rapid repeats.</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA</strong> – For cron triggers, set <code>cronTimezone</code> to a fixed zone (e.g., <code>UTC</code>) on all nodes to avoid overlapping runs.</p>
<h3 style="margin-bottom: 40px; line-height: 1.3;">5.3 Monitoring Queue Backlog (2 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">redis-cli -h redis llen bull:default # pending jobs
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">If the length exceeds <strong>500</strong>, consider scaling n8n replicas or increasing Redis <code>maxmemory</code>.</p>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">6. Troubleshooting Common Sync Issues</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Symptom</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Likely Cause</th>
<th style="border: 1px solid #e0e0e0; padding: 13px;">Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Duplicate API calls</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Two nodes processing the same queue item</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Verify a single broker address (<code>QUEUE_BULL_REDIS_HOST</code>) and that <code>EXECUTIONS_PROCESS=queue</code> is set on <strong>all</strong> nodes.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Workflow not found after scaling</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Workers pointing to different DB instances</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Use one connection string (<code>DB_POSTGRESDB_HOST</code>) and confirm DNS resolves to the same service.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Credentials decryption error</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Mismatched <code>N8N_ENCRYPTION_KEY</code> across pods</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Export the key from a working node and propagate it uniformly.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Stale static data after pod restart</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Persistent volume not shared</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Switch to DB‑backed static data (default with external DB) or mount a <strong>ReadWriteMany</strong> PVC (e.g., NFS) across pods.</td>
</tr>
<tr>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Redis connection timeout</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Network policy blocks port 6379</td>
<td style="border: 1px solid #e0e0e0; padding: 13px;">Add an allow rule for the Redis service in your cluster’s network policy.</td>
</tr>
</tbody>
</table>
<h3 style="margin-bottom: 40px; line-height: 1.3;">6.1 Diagnostic Script: Part 1 (4 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">#!/usr/bin/env bash
echo "=== DB connectivity ==="
psql -h $DB_POSTGRESDB_HOST -U $DB_POSTGRESDB_USER -d $DB_POSTGRESDB_DATABASE -c "\dt"
</pre>
<h3 style="margin-bottom: 40px; line-height: 1.3;">6.2 Diagnostic Script: Part 2 (4 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">echo "=== Redis queue length ==="
redis-cli -h $QUEUE_BULL_REDIS_HOST llen bull:default
</pre>
<h3 style="margin-bottom: 40px; line-height: 1.3;">6.3 Diagnostic Script: Part 3 (4 lines)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">echo "=== Encryption key consistency ==="
grep N8N_ENCRYPTION_KEY /proc/$(pgrep -f n8n)/environ | cut -d= -f2 | sort | uniq -c
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Run the three parts on each node; outputs should be identical.</p>
<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;">By centralizing workflow definitions in PostgreSQL and delegating trigger handling to a single Redis (or RabbitMQ) queue, every n8n instance works from the same source of truth and processes each event exactly once. Consistent <code>N8N_ENCRYPTION_KEY</code>, shared static‑data storage, and health‑checked pods complete the production‑ready recipe. Apply the snippets above, verify the diagnostics, and your scaled n8n fleet will run deterministically, free from duplicate actions and race conditions.</p>
Step by Step Guide to solve multi instance sync
Who this is for: DevOps engineers or platform engineers who need to run n8n in a horizontally‑scaled Docker/Kubernetes environment with deterministic execution. Keep workflow state consistent across scaled instances. We cover this in detail in the n8n Performance & Scaling Guide.
Sync Setup
Goal
Minimal Config
Required Services
Key Env Vars
Consistent workflow execution across 3+ n8n nodes
Use PostgreSQL for DB + Redis (or RabbitMQ) for queue
Restart all nodes – they now share workflow definitions and pull jobs from a single queue, guaranteeing state consistency.
Quick Diagnosis: What Problem Does This Page Solve?
If you encounter any database optimization resolve them before continuing with the setup.
When you horizontally scale n8n (Docker Swarm, Kubernetes, or multiple VMs), each node keeps its own execution cache. Without coordination the same workflow can fire on several nodes simultaneously, causing duplicate actions, race conditions, and data drift.
Solution – Centralize workflow definitions, execution state, and queue handling using a shared PostgreSQL database and a distributed job broker (Redis or RabbitMQ). The guide below shows the exact configuration, minimal code snippets, and troubleshooting steps to achieve deterministic, conflict‑free execution across any number of n8n instances.
1. Execution Model Overview
1.1 Workflow Storage
Mode
Storage Backend
Note
Single‑instance
SQLite (local file)
Not shareable across containers
Multi‑instance
PostgreSQL / MySQL / MariaDB
Network‑accessible DB required
1.2 Execution Queue
Mode
Queue Implementation
Benefit
Single‑instance
In‑process (synchronous)
Simple, but no deduplication
Multi‑instance
Bull (Redis) or RabbitMQ
Guarantees exactly‑once processing
1.3 Static Data & Credentials
Storage
Location
Recommendation
Local files (/root/.n8n)
Container FS
Do not use in scaled setups
DB‑backed tables (staticData)
Shared DB
Default when DB_TYPE is external
When EXECUTIONS_PROCESS=queue is set, n8n publishes every trigger event to the broker. Any node can pull the job, ensuring a single execution per trigger. If you encounter any database migration strategies resolve them before continuing with the setup.
2. Choosing a Shared Persistence Layer
2.1 Database Options
DB Engine
Pros
Cons
PostgreSQL
Strong ACID, JSONB, SELECT FOR UPDATE locking
Slightly heavier
MySQL / MariaDB
Familiar to many DBAs
Limited JSON querying (MySQL 5.7+)
SQLite (shared NFS)
Zero‑config
File‑locking issues under concurrency – not recommended
EEFA – PostgreSQL’s FOR UPDATE SKIP LOCKED prevents workers from stealing each other’s execution rows, eliminating duplicate runs.
2.2 Queue Broker Options
Broker
Typical Latency
Scaling Ease
Persistence
Redis (Bull)
< 5 ms
Horizontal scaling trivial
In‑memory, optional RDB/AOF
RabbitMQ
~ 10 ms
Supports complex routing
Durable queues
Kafka
~ 1 ms (high‑throughput)
Very large clusters
Log‑based persistence – overkill for most n8n workloads
EEFA – Reuse an existing Redis cache if available; otherwise RabbitMQ gives built‑in dead‑letter handling for failed jobs. If you encounter any cache layer implementation resolve them before continuing with the setup.
Run the three parts on each node; outputs should be identical.
Conclusion
By centralizing workflow definitions in PostgreSQL and delegating trigger handling to a single Redis (or RabbitMQ) queue, every n8n instance works from the same source of truth and processes each event exactly once. Consistent N8N_ENCRYPTION_KEY, shared static‑data storage, and health‑checked pods complete the production‑ready recipe. Apply the snippets above, verify the diagnostics, and your scaled n8n fleet will run deterministically, free from duplicate actions and race conditions.