Overview

Flowsave is an open-source CLI for self-hosted n8n instances. It snapshots your workflows, folder structure, and encrypted credentials to local disk, lets you compare and restore any snapshot, migrate a full instance to a new server in one command, and optionally version your backups in a Git repository.

Who it's for: n8n self-hosters — anyone running n8n in Docker, on a VPS, or on bare metal who has ever lost a workflow to a bad upgrade, a deleted container, or an accidental edit.

Design principles

  • Local-first. All snapshots live on your machine. No telemetry, no phoning home, no account required.
  • Credentials are sacred. Always encrypted at rest (AES-256-GCM), never in logs, never pushed to git.
  • One binary, zero dependencies. The npm package is a single self-contained file.
  • Honest output. Every command ends with a summary that says exactly what happened — including what didn't happen and why.

Installation

npm install -g flowsave-cli
  • The npm package is flowsave-cli; the installed command is flowsave.
  • Requires Node.js ≥ 18.
  • The n8n instance must be reachable over HTTP(S) from the machine running Flowsave.

Docker requirement (credential backup only)

Workflow backup needs only the n8n REST API. Credential backup and restore additionally use docker exec to call the n8n CLI inside the container. On Linux, Docker must be usable without sudo:

Linux one-time setup
sudo usermod -aG docker $USER   # then log out and back in

macOS with Docker Desktop requires nothing — it already runs as the current user. Run flowsave doctor to verify the entire setup before taking your first backup.

Quick start

Three commands to go from zero to your first verified snapshot:

flowsave config init      # one-time interactive setup
flowsave backup           # take your first snapshot
flowsave list             # see all snapshots

Core concepts

Snapshot

A point-in-time copy of an n8n instance: all workflows as individual JSON files, optional folder structure, optional encrypted credentials, and metadata. Snapshots get sequential integer IDs (1, 2, 3, …) and live under the configured backup directory.

Configuration

A single JSON file at ~/.flowsave/config.json, created by the interactive wizard (flowsave config init). All commands read it; commands that require it exit with a pointer to config init if it is missing.

Credential encryption

Credentials are exported from the n8n container, encrypted with a user-supplied passphrase, and stored as a single opaque blob. The passphrase is never stored anywhere. Flowsave cannot recover credentials if it is lost — this is a feature, not a limitation. Workflows are not encrypted and are unaffected by a lost passphrase.

Two credential transport paths

PathHow it worksLimitation
Docker docker exec into the container, runs n8n export/import:credentials. Handles all credential types including OAuth. Requires Docker to be running locally and the container to be accessible.
API n8n REST API. No Docker needed; works across machines. OAuth credentials may fail schema validation — their exported data contains internal token state the API rejects. Failures are listed by name so you can re-add them manually.

n8n editions

The folder REST API is an n8n Enterprise feature. On Community edition, backups are complete but flat — folder layout is not preserved. Flowsave detects this and says so in every relevant summary.

Command reference

Jump to any command:

flowsave config

Manage the configuration file at ~/.flowsave/config.json. Three subcommands: init, show, and set.

flowsave config init

Interactive setup wizard. Run once after installing.

PromptDefaultValidation
n8n instance URLhttp://localhost:5678Must parse as a valid URL
API key (n8n Settings → API)Required, non-empty; input masked with *
Docker container nameblankOptional — blank disables credential backup
Backup directory~/.flowsave/backups
Git remote URLblankOptional — blank disables flowsave push
Git branchmainOnly asked if a git remote was entered
If a config already exists, init asks "Overwrite?" (default No) before touching anything. It makes no API calls — connectivity is verified by flowsave doctor or the first backup.

flowsave config show

Prints the current configuration with the API key masked (first 8 + last 4 characters visible). Also prints the config file path. Exits 1 with a pointer to config init if no config exists.

flowsave config set <key> <value>

Updates a single field without re-running the wizard. Valid keys: instanceUrl, apiKey, containerName, backupDir, gitRemote, gitBranch, dashboardToken. Any other key exits 1 and lists the valid keys.

flowsave config set gitRemote git@github.com:you/n8n-backups.git
flowsave config set gitBranch backups
flowsave config set containerName n8n

flowsave backup free

Snapshot all workflows and (optionally) encrypted credentials from the configured instance.

flowsave backup

No flags. Credential backup is controlled entirely by config and an interactive prompt.

Interactive behavior

Only shown when containerName is configured:

  1. Prompt: "Set a passphrase to encrypt credentials (leave blank to skip):" — masked input, validated inline against the passphrase policy. Blank skips credentials entirely.
  2. Prompt: "Confirm passphrase:" — must match exactly; mismatch aborts with exit 1 before anything is written.

Output — Snapshot Summary

  • Snapshot ID, instance URL, n8n version (when detectable via Docker), timestamp, workflow count
  • Folder structure: ✓ included / ✗ not included (Enterprise-only feature)
  • Credentials: ✓ encrypted & included / — not included
  • Snapshot size, saved-to path
Retention warning: when a passphrase is used, Flowsave prints the exact restore command needed later. Credentials in that snapshot are permanently locked to the passphrase entered — there is no recovery path if it is lost.

Exit codes

0 success · 1 passphrase mismatch, connectivity failure, or any backup error.

flowsave restore free

Restore a snapshot to the same instance or to a different one.

flowsave restore 3                                          # same-instance
flowsave restore --snap 3                                    # identical — flag form
flowsave restore 3 --to http://new:5678 --api-key <key>      # cross-instance
flowsave restore 3 --to http://new:5678 --api-key <key> \
  --target-container n8n-new --passphrase <pass>             # cross-instance + Docker creds

Flags

FlagDescription
[id] positionalSnapshot ID to restore
--snap <id>Alternative to the positional argument
--to <url>Target instance URL — switches to cross-instance mode
--api-key <key>Target API key — required with --to, rejected without it
--target-container <name>Local Docker container for cross-instance credential import (handles OAuth and all types)
--passphrase <key>Passphrase to decrypt the snapshot's credentials

Same-instance vs. cross-instance

Cross-instance always creates new workflows — it never updates by ID. Re-running will duplicate everything. The CLI prints this notice on every cross-instance run. Use flowsave diff before migrating again to check what changed.
  • Same-instance (no --to): workflows are updated/created by ID; stale credentials absent from the snapshot are automatically deleted after Docker import.
  • Cross-instance (--to): always creates new workflows (forceCreate).

Exit codes

0 success · 1 validation or restore error.

flowsave migrate free

Take a fresh backup of the live source instance and restore it to a destination in one command. Always migrates current state — for a historical snapshot use restore --to instead.

flowsave migrate --to http://new:5678 --api-key <key>
flowsave migrate --to http://new:5678 --api-key <key> --passphrase <pass>
flowsave migrate --to http://new:5678 --api-key <key> \
  --target-container n8n-new --passphrase <pass>

Flags

FlagRequiredDescription
--to <url>Destination n8n instance URL
--api-key <key>Destination n8n API key
--target-container <name>Local Docker container for credential import (OAuth-safe path)
--passphrase <key>Passphrase for credential encryption/decryption
Why migrate asks to "set" (not "enter") a passphrase: migrate creates a fresh backup, so there is no existing encryption to validate against. The passphrase entered becomes the new encryption for this snapshot. Confirmation prevents a typo from permanently locking the credentials.

Progress: spinner shows "Step 1/2: Backing up source instance…" then "Step 2/2: Restoring to destination instance…". The migration leaves a normal restorable snapshot behind.

Exit codes

0 success · 1 passphrase mismatch, missing required flags, or any step failing.

flowsave list local

List all local snapshots in a table, newest first. No API calls.

flowsave list

Columns: ID · Timestamp · Size · Instance URL (truncated at 40 chars). Empty state prints: "No snapshots yet. Run 'flowsave backup' to take your first snapshot."

flowsave show <id> local

Full details of one snapshot. No API calls.

flowsave show 3

Output includes: date, instance, n8n version (when recorded), workflow count, size, credentials line, folder backup flag, credentials table (names + types, no secrets), and a workflows table with name, active status, node count, folder path, and tags.

Exit codes

0 success · 1 invalid or unknown snapshot ID.

flowsave diff <id1> <id2> local

Compare two snapshots. No API calls.

flowsave diff 3 5

Reports added/removed workflows by name, and for modified workflows shows field-level context: node count changes, activation toggles, and renames. When both snapshots carry credential metadata, also reports added and removed credentials by name and type.

Known limitations

  • Credential diff tracks presence only (by ID). Renames are invisible — the data is encrypted; the name is metadata.
  • A credential type change appears as remove + add, matching how n8n itself requires delete-and-recreate.

Exit codes

0 success · 1 non-integer IDs or unknown snapshots.

flowsave delete <id> [id…]

Permanently delete one or more snapshots from disk and the local index.

flowsave delete 3
flowsave delete 19 14 13
flowsave delete 19 14 13 --yes
FlagDescription
-y, --yesSkip the confirmation prompt

Accepts multiple IDs; duplicates are de-duplicated. Unknown IDs are each reported; if some IDs are valid the command continues with those. Without --yes, prints the full list and asks once — default No.

Exit codes

0 success or user-aborted · 1 invalid/missing IDs.

flowsave prune

Remove snapshots whose content is identical to a newer snapshot — keeps the most recent copy of each distinct state.

flowsave prune               # preview + confirm
flowsave prune --dry-run     # preview only, no changes
flowsave prune --yes         # no confirmation prompt
FlagDescription
--dry-runShow candidates without deleting anything
-y, --yesSkip the confirmation prompt

"Identical" means: zero workflows added, removed, or modified and zero credentials added or removed.

How the algorithm works

Prune walks newest → oldest, comparing each snapshot only to the last distinct reference — not the absolute newest. This preserves the last restore point before every deletion/change event.

Example: 6 snapshots, only 3 pruned
#2  9 creds (Airtable present)   ← kept — differs from #3
#3  8 creds (Airtable removed)   ← kept — differs from #6
#4  9 creds                      ← pruned (≡ #6)
#5  9 creds                      ← pruned (≡ #6)
#6  9 creds (Airtable re-added)  ← kept (newest)

Exit codes

0 success, nothing-to-do, or user-aborted · 1 on errors.

flowsave push

Commit the latest snapshot to a git repository and push it to the configured remote.

flowsave push

No flags. Configure the remote first:

flowsave config set gitRemote git@github.com:you/n8n-backups.git
flowsave config set gitBranch backups   # optional, default: main

Key behaviors

  • The remote repo does not need to exist as a branch beforehand — Flowsave initializes a local git repo on first push and creates the remote branch automatically.
  • A .gitignore is written automatically so encrypted credential blobs (_credentials.enc.json) never enter git — only workflow JSON and snapshot metadata are committed.
  • Commit message format: chore: flowsave backup <ISO-timestamp> snapshot-<id>
  • Always pushes even with no new commit — so changing gitBranch propagates to the remote.
  • Git authentication is yours (SSH keys / credential store) — Flowsave never touches or stores git credentials.

Exit codes

0 success · 1 no gitRemote configured or no snapshots exist.

flowsave doctor

Diagnose the local setup. Runs four checks, each reported as ✓/✗. Never throws.

flowsave doctor
#CheckPass conditionFailure detail
1Config~/.flowsave/config.json exists and validatesValidation message; subsequent checks are skipped
2n8n instanceGET /api/v1/workflows?limit=1 succeeds within 5sDistinguishes 401 (connected but API key invalid) from network errors
3Docker (if containerName set)Daemon reachable without sudo and named container is runningPermission-denied gets the exact fix: sudo usermod -aG docker $USER + relogin
4Backup dirDirectory exists/creatable and writablePath and reason

Exit codes

0 all checks pass · 1 any check fails (with count).

Configuration reference

File: ~/.flowsave/config.json — created by config init, edited by config set, readable via config show.

KeyRequiredDefaultDescription
instanceUrlhttp://localhost:5678Base URL of the n8n instance
apiKeyn8n API key (n8n Settings → API). Masked in all output.
containerNameunsetDocker container name. Enables credential backup/restore via docker exec. Optional — omit for workflow-only backups.
backupDir~/.flowsave/backupsLocal snapshot storage. ~ is expanded.
gitRemoteunsetGit remote URL for flowsave push.
gitBranchmainBranch pushed to.
dashboardTokenunsetReserved for agent/dashboard connectivity (upcoming).

Security model

Encryption at rest

  • Cipher: AES-256-GCM (authenticated encryption — tampering is detected, not just hidden)
  • Key derivation: scrypt, cost N=16384, r=8, p=1 — deliberately slow against brute force
  • Per-snapshot randomness: fresh 32-byte salt and 12-byte IV on every encryption
  • Blob layout: [salt 32B][iv 12B][authTag 16B][ciphertext] — one self-contained file, _credentials.enc.json

Passphrase policy

Enforced in core — no CLI path can bypass it:

  • Minimum 12 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one digit or special character

Validation happens inline in the prompt (immediate feedback) and again inside encrypt().

Passphrase lifecycle

  • Entered twice (set + confirm) for backup and migrate; mismatch aborts before anything is written
  • Never stored, never logged, never in error messages, never in any persisted file
  • Lost passphrase = unrecoverable credentials for that snapshot, by design. Workflows are unaffected.

What never leaves your machine

  • Encrypted credential blobs are excluded from flowsave push via .gitignore
  • No telemetry of any kind — the only network calls are to your own n8n instances and your own git remote

Storage layout

~/.flowsave/
├── config.json                  # configuration (API key in plain text — local file)
├── index.json                   # snapshot index: id, timestamp, instanceUrl, sizeBytes
└── backups/                     # = backupDir (configurable)
    ├── .git/                    # created by flowsave push (optional)
    ├── .gitignore               # excludes _credentials.enc.json
    └── <id>/                   # one directory per snapshot
        ├── meta.json            # snapshot metadata (counts, versions, flags)
        ├── <Workflow Name>.json # one file per workflow (root = no folder)
        ├── <Folder>/…          # subdirectories mirror n8n folders (Enterprise)
        ├── _credentials.enc.json   # encrypted credential blob (optional)
        └── _credentials.meta.json  # credential names+types, NO secrets (optional)

Workflow JSON files are the raw n8n export format — they round-trip losslessly and are readable in any editor or git UI.

Error handling & exit codes

Flowsave never shows a stack trace. Every error message is human-readable with an actionable hint when one exists.

SituationMessage styleHint
Invalid/missing configThe specific validation errorRun flowsave config init
Network failure (ECONNREFUSED, ETIMEDOUT, etc.)Cannot reach n8n instance: <detail>Run flowsave doctor
Wrong passphrase on restoreDecryption failure (GCM auth tag mismatch)
Anything elseThe error's own message

Exit code summary

  • 0 — success, nothing-to-do, or user answered "No" to a confirmation
  • 1 — any validation failure, aborted passphrase confirmation, failed health check, or runtime error

Exit codes are stable and suitable for use in scripts and cron jobs.

Edition compatibility

FeatureCommunity (free)Enterprise
Workflow backup & restore
Credential backup (source)✓ requires Docker on source
Credential restore — same-instance✓ docker exec + auto-prune stale
Credential restore — cross-instance (API)✓ simple creds; OAuth may fail
Credential restore — cross-instance (Docker)✓ with --target-container
Folder structure in backup
Folder structure on restore

The folder REST API is license-gated by n8n. Community backups lose nothing — the layout is simply flat.

Troubleshooting

"Cannot reach n8n instance"

Run flowsave doctor. It checks the URL, API key (distinguishes an invalid key from a network failure), Docker, and backup directory in one run.

"Permission denied" on credential backup (Linux)

Your user isn't in the docker group. Fix:

sudo usermod -aG docker $USER   # then log out and back in

Doctor detects and prints exactly this fix.

OAuth credentials failed during cross-instance restore/migrate

Expected behavior on the API path — their exported data carries internal token state the n8n API rejects. Either re-add them manually in the target UI, or re-run with --target-container <name> if the destination container is locally accessible (the Docker path has no schema restrictions).

I forgot the passphrase for snapshot N

Credentials in that snapshot are unrecoverable — nothing is stored that could recover them, by design. Workflows are unaffected. Take a new backup with a new passphrase.

Re-running migrate/cross-instance restore duplicated my workflows

Documented behavior: cross-instance operations always create, never update. Compare first with flowsave diff, then clean up duplicates on the target manually.

flowsave push says nothing changed but I switched branches

It still pushes. The remote branch is created or updated even without a new commit.

Snapshot says "names not available — older snapshot"

Credential name metadata (_credentials.meta.json) was introduced after that snapshot was taken. The credentials themselves are intact and fully restorable.

Why doesn't my snapshot show the n8n version?

Version detection needs docker exec (the public REST API doesn't expose it). Configure containerName to record it.

Glossary

TermDefinition
SnapshotOne point-in-time backup with a sequential integer ID.
Same-instance restoreRestore to the configured instance; updates workflows by ID.
Cross-instance restoreRestore to another instance (--to); always creates new workflows.
Docker pathCredential transport via docker exec + n8n CLI; handles all credential types.
API pathCredential transport via n8n REST API; no Docker needed, OAuth-limited.
PruneRemoval of consecutive content-identical snapshots.
Retention warningPost-backup/migrate notice that the snapshot's credentials are locked to the entered passphrase.