8 min read

GitOps Backups for n8n: 30‑Minute Schedule + One‑Click Restore

Table of Contents

đź’ˇ

Goal in plain English: you’ll build two flows for a self‑hosted n8n(1) a scheduled backup to Git every 30 minutes and (2) a manual restore that re‑hydrates workflows and credentials. Keep your N8N_ENCRYPTION_KEY safe and use a private repo.

What You’ll Build

What you’ll learn: How to create automated Git backups for n8n that run every 30 minutes, plus a one-click restore system for safe rollbacks.

You’ll finish with reliable Git history for every n8n change. Roll back in minutes, not hours.

  • Two flows: Scheduled Backup (every 30 minutes) and Manual Restore
  • Benefits: versioned configs, quick diffs, safe rollbacks after upgrades
  • Tricky part: credentials are encrypted with N8N_ENCRYPTION_KEY. Don’t lose it

If you rotate the encryption key without re‑encrypting, restores won’t decrypt old credentials.

Here’s the complete backup and restore system architecture:

flowchart TD
    A[30-Minute Cron Trigger] --> B[Export Workflows]
    B --> C[Export Credentials]
    C --> D[Git Add Changes]
    D --> E[Git Commit]
    E --> F[Git Push to Remote]
    
    G[Manual Restore Trigger] --> H[Git Pull Latest]
    H --> I[Import Credentials]
    I --> J[Import Workflows]
    J --> K[n8n Ready]
    
    F --> L[Git History]
    L --> G
    
    classDef backup fill:#e1f5fe,stroke:#01579b
    classDef restore fill:#fff3e0,stroke:#ef6c00
    classDef success fill:#e8f5e8,stroke:#2e7d32
    
    class A,B,C,D,E,F backup
    class G,H,I,J restore
    class K,L success

Prerequisites and Setup

You’ll prepare Git access, volumes, and the n8n CLI.

  1. Secrets and environment
  • Generate and set a strong key. Use the same key across environments.
  • Keep repo private. Never push decrypted secrets.
  • Provide Git access via SSH deploy key or HTTPS PAT.
# One-time, on your host
openssl rand -hex 32 # copy this value as N8N_ENCRYPTION_KEY
  1. Docker Compose baseline
  • Persist /home/node/.n8n for state and mount a backups folder.
  • Inject the encryption key with env vars.
  • Pin an image tag you trust.
# docker-compose.yml (excerpt)
services:
  n8n:
    image: n8nio/n8n:latest
    environment:
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=UTC
    volumes:
      - n8n_data:/home/node/.n8n
      - ./backups:/home/node/backups
      - ./git-ssh:/home/node/.ssh:ro # optional for SSH deploy key
    ports:
      - "5678:5678"
volumes:
  n8n_data:
  1. Initialize the Git repo
  • Use a dedicated repo like n8n-config-backups.
  • Prefer SSH with a read/write deploy key for the n8n box.
# On the host (or inside the container working in /home/node/backups)
cd backups
git init
git remote add origin [email protected]:your-org/n8n-config-backups.git
git config user.name "n8n-bot"
git config user.email "[email protected]"

# Optional: prevent accidental plaintext dumps
printf "\n# safety\n*.decrypted.json\n" >> .gitignore
  1. Verify the n8n CLI
  • The CLI ships with n8n. Run it inside the container.
  • You’ll call it from Execute Command nodes later.
# From your host
docker compose exec n8n n8n --version

Two quick comparison choices you’ll make:

DecisionOption A (Recommended)Option B
Git transportSSH deploy key (no tokens in URLs)HTTPS with PAT (store in credential helper)
Export formatSeparate files per item (clean diffs)Single JSON files (simpler to handle)

Bottom line: lock down secrets first, then wire Git.


Scheduled Backup Workflow

You’ll export everything then push to Git on a cron. Keep it idempotent.

  1. Create workflow and trigger
  • Name: Backup to Git (*/30 minutes).
  • Add node: Schedule Trigger - Mode: Cron.
  • Cron examples:
*/30 * * * *      # every 30 minutes (UTC)
0 */1 * * *       # top of every hour (fallback)
  1. Export workflows to disk
  • Add node: Execute Command (name: Export Workflows).
  • Why CLI: consistent, future‑proof, respects encryption.
  • Command (exports as separate JSON files):
mkdir -p /home/node/backups/workflows && \
  n8n export:workflow \
    --all \
    --output=/home/node/backups/workflows \
    --separate
  1. Export credentials to disk (still encrypted)
  • Add node: Execute Command (name: Export Credentials).
  • Do not add any flag that decrypts secrets.
mkdir -p /home/node/backups/credentials && \
  n8n export:credentials \
    --all \
    --output=/home/node/backups/credentials \
    --separate
  1. Write a timestamped commit message
  • Add node: Set (name: Commit Meta).
  • Fields:
    • key: msg, value: Backup: 12/10/2899
  1. git add / commit / push
  • Add node: Execute Command (name: Git Push).
  • Use working directory /home/node/backups.
  • Command:
cd /home/node/backups && \
  git add -A && \
  git commit -m "{{ $json.msg }}" || echo "nothing to commit" && \
  git pull --rebase origin main && \
  git push origin HEAD:main
  1. Recommended node options (pseudo‑JSON)
{
  "Export Workflows": {"shell": "/bin/bash", "continueOnFail": false, "workingDirectory": "/home/node"},
  "Export Credentials": {"shell": "/bin/bash", "continueOnFail": false, "workingDirectory": "/home/node"},
  "Git Push": {"shell": "/bin/bash", "continueOnFail": false, "workingDirectory": "/home/node/backups"}
}

Small extras that help in real life:

  • Add a Wait node before Git to avoid race conditions in slow disks.
  • Send a Notification (Slack/Email) on failure using the Error Trigger.
  • Store branch name in a Global Static Data key if you use multiple branches.

A quick alternative if you prefer nodes over shell:

TaskExecute CommandGit (community) node
Setup effortMinimalExtra install/permissions
FlexibilityHighestMedium
Observability in logsClearClear

In short, the shell path is simple and robust.


Manual Restore Workflow

You’ll manually trigger a Git pull, then import credentials first, workflows second.

  1. Create workflow and trigger
  • Name: Manual Restore from Git.
  • Add node: Manual Trigger.
  1. Pull the latest backup
  • Add node: Execute Command (name: Git Pull).
cd /home/node/backups && \
  git fetch origin && \
  git checkout main && \
  git reset --hard origin/main
  1. Import credentials
  • Add node: Execute Command (name: Import Credentials).
  • Why first: workflows may reference credential IDs.
n8n import:credentials \
  --input=/home/node/backups/credentials
  1. Import workflows
  • Add node: Execute Command (name: Import Workflows).
n8n import:workflow \
  --input=/home/node/backups/workflows
  1. Guardrails and prompts
  • Add node: IF to confirm branch equals the expected one.
  • Add node: Set to log operator, reason, and timestamp.
  • Optional: put this flow behind n8n User Management so only admins see it.

A simple success message wraps it up:

âś… Restore completed at {{$now.utc.toISO()}}

Testing and Best Practices

You’ll prove it works before risky changes. Then you’ll add polish.

  1. Test safely end‑to‑end
  • Create a dummy workflow called TEST_DELETE_ME.
  • Run the Backup flow manually via the Execute Workflow button.
  • Confirm Git shows new/updated files and a fresh commit.
  • Delete TEST_DELETE_ME in n8n.
  • Run Manual Restore. Check the workflow returns with the same ID/name.
# Sanity check exports exist inside container
ls -la /home/node/backups/workflows | head
ls -la /home/node/backups/credentials | head
  1. Security do’s and don’ts
  • Do keep the repo private and limit write access.
  • Do back up the N8N_ENCRYPTION_KEY in a secure vault.
  • Don’t export decrypted credentials to Git. Avoid any —decrypted flag.
  • Don’t embed PATs in remote URLs if you can use SSH.

“If it’s not in Git it didn’t happen.” True. Also, if secrets leak to public Git it’s game over. Guard that line.

  1. Versioning and environments
  • Use branches: main (prod), develop (staging), feature/* for experiments.
  • Tag releases before major n8n upgrades.
  • Consider one repo per environment if access boundaries differ.
  1. Operational extras
  • Add a Cron health check by emitting a heartbeat to your monitoring stack.
  • Pipe stderr from Execute Command into a Slack/Teams alert on failure.
  • Add a pre-commit hook to block files matching *.decrypted.json.
  1. Handy snippets
# One-liner to run backup immediately (inside container)
bash -lc "n8n export:workflow --all --output=/home/node/backups/workflows --separate && n8n export:credentials --all --output=/home/node/backups/credentials --separate && cd /home/node/backups && git add -A && git commit -m 'Manual backup' || true && git push origin HEAD:main"
  1. Next steps
  • Add a staging/prod split with protected branches.
  • Wire CI to validate JSON shape on PRs.
  • Extend backups to binary assets you reference (templates, custom nodes).
đź’ˇ

You’re ready to ship changes with confidence. Keep the repo private, back up the N8N_ENCRYPTION_KEY offline, and monitor your backup job so you never fly blind.

đź“§