Overview
@egose/n8n-sync syncs workflows, credentials, and optionally execution summaries between n8n instances using n8n external hooks. It builds two self-contained CommonJS hook bundles that you deploy alongside n8n and point at via EXTERNAL_HOOK_FILES.
| Bundle | Role |
|---|---|
dist/publisher.cjs | Runs on the source instance. Lifecycle hooks POST sync events to one or more subscribers. HTTPS is required outside development/test. |
dist/subscriber.cjs | Runs on each target instance. Mounts an endpoint on n8n's own server and applies events via n8n's internal repositories. |
How it works
┌──────────────┐ credentials.create/update/delete ┌──────────────┐
│ source n8n │ workflow.afterCreate/afterUpdate/… │ target n8n │
│ │ ──────────────────────────────────────► │ (1..n) │
│ publisher.cjs│ POST /rest/sync/v1/events │subscriber.cjs│
│ │ HMAC-signed or bearer-token auth │ │
└──────────────┘ └──────────────┘
- Fan-out: the publisher delivers every event to every URL in
SYNC_SUBSCRIBER_URLS. Hook preparation and emission are serialized per source entity, and each target has its own serialized in-memory queue. Slow or unreachable targets do not delay other targets. - Fire-and-forget hooks: deliveries run in the background and failures are retried with exponential backoff (1s, 2s, 4s, capped at 10s) then logged, so a sync outage cannot break n8n operations. Publisher hooks never throw.
- Durable event identity: every emitted event includes
sourceId,eventId, and a monotonic per-entityentityRevision. Publisher counters live underSYNC_PUBLISHER_STATE_PATH; subscriber checkpoints and tombstones live underSYNC_SUBSCRIBER_STATE_PATH. - Idempotent subscriber: the subscriber applies events through the target instance's own TypeORM repositories, resolved from n8n's DI container inside
n8n.ready. - Encrypted credentials only: credential
datais passed through as the stored encrypted string blob. All instances must share the sameN8N_ENCRYPTION_KEYso targets can decrypt secrets at runtime.
Synced entities
By default n8n-sync keeps workflows and credentials mirrored across instances. Execution sync is opt-in because it is high-volume.
| Entity | Wired by default | Opt-in env |
|---|---|---|
| Workflows | yes | SYNC_ENTITIES (remove workflows to disable) |
| Credentials | yes | SYNC_ENTITIES (remove credentials to disable) |
| Executions ★ | no | SYNC_ENTITIES=...,executions |
★ workflow.postExecute fires per execution (high volume) and the publisher handler is fire-and-forget so it never blocks n8n. Only scalar lifecycle columns are mirrored; per-step fullRunData is dropped. executions requires workflows to be enabled.
Supported runtime
The subscriber runtime adapter is pinned and contract-tested against n8n 2.31.2. The package lazy-loads @n8n/di and @n8n/db from the official Docker image paths by default, with N8N_DI_PATH and N8N_DB_PATH overrides for custom layouts.
Where to go next
- Quick Start — build the bundles and wire up source + target in under a minute.
- Architecture — publisher fan-out, subscriber mounted routes, and the
SyncEventwire format. - Wired Hooks — the full list of n8n hooks that trigger sync events.
- Environment Variables — every
SYNC_*andN8N_*knob. - Persistence & Readiness — state files, readiness probes, and recovery guidance.
- Limitations — what sync cannot and does not try to do.