Skip to main content

Limitations

n8n-sync is intentionally narrow: it is a one-way, eventually-consistent mirror of workflow, credential, and (opt-in) execution state across n8n instances. These are the things it explicitly does not do, and the platform reasons why.

n8n-platform quirks

  • Deactivation does not sync. n8n fires no external hook on workflow deactivation; deactivateWorkflow only emits an internal event. The target corrects state on the next update or activate event, or stays active until then. Do not try to work around this with polling.
  • workflow.activate fires pre-commit. If a later hook rejects the activation, the subscriber may briefly hold an uncommitted state; the next event converges it. The applier treats activate as an upsert so state converges.
  • Repository access happens only inside the n8n.ready hook, where n8n's DI Container is initialized. Resolving it earlier crashes the subscriber.

Wire / delivery semantics

  • One-way, last-write-wins. Sync is directional. Upserts carry the source monotonic timestamp (updatedAt for workflows/credentials, stoppedAt for executions) and are skipped when the target row is already at or beyond it, so out-of-order or duplicate deliveries cannot regress state. Deletes and archives are applied unconditionally.
  • The delivery queue is in-memory. Events queued but not yet delivered when the source instance restarts are lost; state converges on the next event for that entity (or stays divergent until then).
  • Active state is DB-only. With SYNC_APPLY_ACTIVE_STATE=true, the target's active flag is written to the database, but triggers/webhooks are not registered with the target's active workflow manager until restart or manual toggle. Keep it false (the default) for passive-standby targets.
  • Deletes and archives for unknown IDs are no-ops. update/delete on missing rows return early; sync is eventually consistent by design.

Credentials

  • Credential sync requires a shared N8N_ENCRYPTION_KEY on all instances. Credential data is an encrypted blob passthrough — the publisher never decrypts it and the subscriber stores it as-is, so the target instance's key must match the source's for the credential to remain usable at runtime.

Executions

  • Execution sync is summary-only. SYNC_ENTITIES=…,executions upserts a row in the target's execution_entity table with the source ID and scalar lifecycle columns (id, workflowId, status, mode, finished, startedAt, stoppedAt, retry ids, workflowVersionId) plus a best-effort workflow snapshot. The execution_data blob (per-step fullRunData) is not written to keep payloads small; target-side reads via the Public API will see the execution summary but not its run detail.
  • Execution staleness is based on stoppedAt. In-flight executions (running, waiting, new) carry stoppedAt: null; for them the last-write-wins guard is skipped so a later delivery can still converge state. Re-deliveries of the same event are no-ops.
  • startedAt / createdAt are immutable post-insert on execution_entity — the applier mirrors n8n's own updateExistingExecution semantics and drops them from update payloads.

Auth

  • Auth modes do not cross-accept. A token-mode subscriber rejects hmac-signed requests and vice versa. Both sides must use the same SYNC_AUTH_MODE. See Authentication.

Project assignment

  • SYNC_TARGET_PROJECT_ID (default empty) — when set, newly created workflows/credentials are linked to that project. When empty, the applier falls back to the target instance owner's personal project (resolved lazily via UserRepository + ProjectRepository.getPersonalProjectForUser, cached for the process lifetime including the negative case). The fallback makes synced entities visible through the target's Public API without explicit configuration. An explicit SYNC_TARGET_PROJECT_ID always wins.

Filtering

  • Tag filtering is source-side only. SYNC_FILTER_BY_TAG rewrites the publisher's DTOs and may turn an upsert into a delete; the subscriber never sees or honors tag fields. Tagging a workflow on the source does not propagate the tag itself to the target. See Tag-based Filtering.

Reference