IAnswerable

Back Up n8n Workflows to Git

04 Dec 2025

n8n’s database holds your workflows, but “it’s in the database” is not a backup strategy — it’s a single point of failure with extra steps. A bad edit that breaks a workflow, or a database restore from the wrong point in time, and you’re rebuilding logic from memory. Exporting workflows to git gives you version history, diffs, and a revert that actually works.

Why the database alone isn’t enough

A Postgres or SQLite backup protects you from losing the whole instance. It doesn’t protect you from “I edited this workflow yesterday and it’s now broken and I don’t remember what I changed.” Database backups are usually daily snapshots at best — they won’t have hourly granularity, and restoring one rolls back everything in the instance, not just the one workflow you broke. Git gives you a diff per change and lets you revert one workflow without touching anything else.

1. Export workflows to JSON

n8n’s CLI can dump every workflow as individual JSON files:

docker compose exec n8n n8n export:workflow --all --output=/home/node/.n8n/backup/ --separate

--separate is the flag that matters here — without it, you get one giant JSON array with every workflow crammed into a single file, which makes git diffs useless. You want one file per workflow so a change to one workflow shows up as a change to one file.

2. Set up the git repo

mkdir -p ~/n8n-backups
cd ~/n8n-backups
git init
git remote add origin git@github.com:you/n8n-backups.git

Use a private repo — workflow JSON can contain node configuration that references internal URLs, even if credentials themselves are stored separately and encrypted.

3. Script the export and commit

#!/bin/bash
set -euo pipefail

BACKUP_DIR=~/n8n-backups
docker compose exec -T n8n n8n export:workflow --all --output=/home/node/.n8n/backup/ --separate
docker cp $(docker compose ps -q n8n):/home/node/.n8n/backup/. "$BACKUP_DIR/workflows/"

cd "$BACKUP_DIR"
git add workflows/
if ! git diff --cached --quiet; then
  git commit -m "Backup: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
  git push origin main
fi

The git diff --cached --quiet check matters — without it, you commit an empty “backup” every single run even when nothing changed, and your history becomes useless noise instead of a record of actual edits.

4. Run it on a schedule

crontab -e
0 */6 * * * /home/you/n8n-backups/backup.sh >> /var/log/n8n-backup.log 2>&1

Every six hours is reasonable for most setups — tune it to how often you’re actually editing workflows. The >> log redirect matters more than it looks: without it, a failing cron job fails silently and you find out your backups stopped weeks ago, at the exact moment you need one.

5. Reverting a bad edit

This is the actual payoff. Find the last good version:

git log --oneline -- workflows/sync-orders-to-crm.json
git show <commit>:workflows/sync-orders-to-crm.json > /tmp/restore.json

Then re-import just that one file:

docker cp /tmp/restore.json $(docker compose ps -q n8n):/home/node/.n8n/restore.json
docker compose exec n8n n8n import:workflow --input=/home/node/.n8n/restore.json

This overwrites the existing workflow with the ID it was exported with — n8n matches on workflow ID, not name, so re-importing an exported file updates the same workflow rather than creating a duplicate.

What this doesn’t cover

Credentials aren’t part of this backup by design — export:workflow doesn’t include them, and exporting credentials to a git repo, even a private one, means committing encrypted secrets to version history forever, which most security-conscious setups will still tell you not to do. Back up credentials separately, on a schedule, to somewhere with proper access controls (a secrets manager, an encrypted volume) — not the same repo as your workflow JSON.