n8n Backup Restore Workflow Database Migration Guide

I back up my n8n workflows every night because losing a workflow is like losing a piece of your operational brain. Here’s the exact backup and migration strategy I’ve refined over two years of running n8n in production.

Backups serve two purposes. First, disaster recovery — if the server dies, you restore from the last backup. Second, migration — moving workflows between environments without rebuilding them from scratch. Both require the same tools, just different timing.

The n8n data model separates workflows, credentials, and execution history. Each lives in a different place. A complete backup captures all three.

What Needs Backing Up

Three components make up a full n8n backup:

1. Workflows — the visual automation definitions, node configurations, and connections
2. Credentials — API keys, OAuth tokens, and authentication secrets
3. Database — workflow metadata, execution history, and system configuration

Workflows and credentials are exportable via the CLI or API. The database depends on your storage backend — SQLite for local installs, PostgreSQL for production deployments.

I back up workflows and credentials manually through the CLI. The database backs up automatically through the underlying storage engine. This separation gives me granular control over what gets restored and when.

Exporting Workflows with the CLI

The n8n CLI provides straightforward export commands:

`bash

Export a single workflow

n8n export –workflowId=123 –output=./backups/workflow-123.json

Export all workflows to a single file

n8n export –all –output=./backups/all-workflows.json

Export workflows to individual files

n8n export –all –output-dir=./backups/workflows/
`

The individual file export creates one JSON file per workflow, named by workflow ID. This is cleaner for version control — each workflow gets its own file in a Git repository.

Include timestamps in your backup filenames for easy identification:

`bash
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
n8n export –all –output=”./backups/all-workflows-${TIMESTAMP}.json”
`

The exported JSON includes the workflow name, active status, nodes, connections, and settings. It does not include credential values — those are stored encrypted in the n8n database and exported separately.

Exporting Credentials

Credentials require a separate export command:

`bash

Export all credentials

n8n export –credentials –output=./backups/all-credentials.json

Export specific credential type

n8n export –credentials –filter=apiKey –output=./backups/api-keys.json
`

The credential export includes the credential name, type, and encrypted data. The encryption key is stored in the n8n environment variable N8N_ENCRYPTION_KEY. Without this key, restored credentials are useless.

Store the encryption key separately from the credential backup:

`bash

Save encryption key to a secure location

echo “$N8N_ENCRYPTION_KEY” > ./backups/encryption-key.txt
chmod 600 ./backups/encryption-key.txt
`

File permission 600 means only the owner can read the file. The encryption key is effectively a master password for all your stored credentials.

Database Backups

The database backup strategy depends on your storage type.

For SQLite (default for local development):

`bash

Stop n8n to ensure consistent backup

systemctl stop n8n

Copy the database file

cp /var/lib/n8n/database.sqlite ./backups/database-$(date +%Y%m%d).sqlite

Restart n8n

systemctl start n8n
`

Stopping n8n during the copy ensures you don’t capture a partially-written database. The copy takes milliseconds for typical workflow databases. For large databases with extensive execution history, consider using SQLite’s built-in backup functionality instead.

For PostgreSQL (production deployments):

`bash

Full database dump

pg_dump -U n8n_user -d n8n_database -F c -f ./backups/postgres-$(date +%Y%m%d).dump

Schema only (no data)

pg_dump -U n8n_user -d n8n_database -s -f ./backups/schema-$(date +%Y%m%d).sql

Data only

pg_dump -U n8n_user -d n8n_database –data-only -f ./backups/data-$(date +%Y%m%d).sql
`

PostgreSQL offers incremental backups through WAL archiving. Enable it in your postgresql.conf:

`conf
wal_level = replica
archive_mode = on
archive_command = ‘cp %p /var/lib/pg_wal_archive/%f’
`

WAL archiving allows point-in-time recovery. You can restore to any moment between backups, not just the last backup point.

Automated Backup Script

Here’s a complete backup script I run nightly via cron:

`bash
#!/bin/bash
set -euo pipefail

BACKUP_DIR=”/var/backups/n8n”
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
RETENTION_DAYS=30

mkdir -p “$BACKUP_DIR/workflows”
mkdir -p “$BACKUP_DIR/credentials”
mkdir -p “$BACKUP_DIR/database”

echo “[$TIMESTAMP] Starting n8n backup…”

Export workflows

n8n export –all –output=”$BACKUP_DIR/workflows/all-$TIMESTAMP.json”

Export credentials

n8n export –credentials –output=”$BACKUP_DIR/credentials/all-$TIMESTAMP.json”

Backup database (SQLite example)

cp /var/lib/n8n/database.sqlite “$BACKUP_DIR/database/db-$TIMESTAMP.sqlite”

Compress old backups

find “$BACKUP_DIR” -name “.json” -o -name “.sqlite” | xargs gzip 2>/dev/null || true

Clean up old backups

find “$BACKUP_DIR” -mtime +$RETENTION_DAYS -delete

echo “[$TIMESTAMP] Backup complete.”
`

Make it executable and schedule with cron:

`bash
chmod +x /usr/local/bin/n8n-backup.sh
echo “0 3 * /usr/local/bin/n8n-backup.sh” | crontab –
`

The script runs at 3 AM daily. It exports workflows, credentials, and the database. Old backups compress automatically. Anything older than 30 days gets deleted.

For PostgreSQL, replace the SQLite copy command with the pg_dump command from the previous section.

Restoring from Backup

Restore follows the same sequence as export, but in reverse order:

`bash

1. Stop n8n

systemctl stop n8n

2. Restore database

cp ./backups/database/db-20260701.sqlite /var/lib/n8n/database.sqlite

3. Start n8n

systemctl start n8n

4. Wait for n8n to be ready

sleep 10

5. Import workflows

n8n import –all –input=./backups/workflows/all-20260701.json

6. Import credentials

n8n import –credentials –input=./backups/credentials/all-20260701.json
`

The database must be restored before workflows. Workflows reference database entries for credential IDs and execution history. Restoring workflows first creates orphaned references.

If you’re migrating to a new server, the process is similar but includes environment setup:

`bash

1. Install n8n on new server

npm install -g n8n

2. Configure environment (database, encryption key, etc.)

export N8N_ENCRYPTION_KEY=”your-encryption-key-here”
export DB_TYPE=postgresdb
export DB_POSTGRESDB_HOST=new-server-db.example.com

3. Start n8n to initialize database

n8n start &
sleep 15

4. Import workflows and credentials

n8n import –all –input=./backups/workflows/all-20260701.json
n8n import –credentials –input=./backups/credentials/all-20260701.json

5. Verify import

n8n export –all –output=./verify-export.json
diff ./backups/workflows/all-20260701.json ./verify-export.json
`

The diff command shows any differences between the backup and the restored export. If they match, the migration succeeded.

Migration Between Environments

Moving workflows from development to production follows the same export/import pattern. The key differences:

1. Different database connections — production uses PostgreSQL, development uses SQLite
2. Different credentials — production API keys differ from development keys
3. Different base URLs — workflow webhook URLs must be updated

Update webhook URLs after migration:

`bash

Find all workflows with webhook nodes

n8n export –all –output=./all-wfs.json

Edit webhook URLs in the JSON

sed -i ‘s|https://dev.n8n.example.com|https://prod.n8n.example.com|g’ ./all-wfs.json

Import edited workflows

n8n import –all –input=./all-wfs.json
`

The sed command replaces all development URLs with production URLs in one pass. Test this on a staging environment before applying to production.

For credential updates, re-authenticate each credential type after migration:

`bash

List all credentials

n8n export –credentials –output=./creds-list.json

Re-authenticate API credentials

(manually through the web UI or API)

`

Re-authenticating ensures the new environment uses current credentials. Old OAuth tokens expire. API keys rotate. Fresh authentication prevents silent failures.

Connecting to Your Existing Setup

If you’re running n8n in Docker, the n8n Docker Deployment guide covers volume mounting for persistent storage. Mount the database directory to a persistent volume to simplify backups.

The n8n Docker Compose production stack includes a backup service configuration. It runs automated backups on a schedule, eliminating the need for a custom cron script.

Understanding n8n’s database architecture helps you choose the right backup strategy. PostgreSQL supports point-in-time recovery. SQLite requires file-level copies.

Action Card: Quick Restore

Emergency restore in three commands:

`bash

1. Stop n8n

systemctl stop n8n

2. Restore database from latest backup

cp /var/backups/n8n/database/db-$(ls /var/backups/n8n/database/ | tail -1) /var/lib/n8n/database.sqlite

3. Start n8n and re-import

systemctl start n8n
n8n import –all –input=/var/backups/n8n/workflows/all-$(ls /var/backups/n8n/workflows/ | tail -1).json
`

This restores the most recent backup. You lose executions that happened between the backup and the failure. That’s acceptable for most recovery scenarios.

References

  • n8n CLI Export/Import Documentation
  • PostgreSQL Backup and Recovery
  • SQLite Backup API
  • n8n Environment Variables
  • Leave a Reply

    Your email address will not be published. Required fields are marked *