Back Up n8n Workflows to Git
04 Dec 2025n8n’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.