How to Back Up and Restore n8n (Database, Credentials and Encryption Key)
To back up n8n, save four things: the n8n data folder (/home/node/.n8n inside the container, which holds the SQLite database and the config file with the encryption key), the Postgres database if you use one, your docker-compose.yml, and your .env file. To restore, put those files back on the same server or a new one and start the stack with the same N8N_ENCRYPTION_KEY. If you lose that key, n8n cannot decrypt your saved credentials.
This guide covers a Docker Compose install on Ubuntu 24.04 or 22.04. It shows how to set up nightly backups, copy them off the server, and restore them. It applies to self-managed installs and to Cloudesta n8n hosting, where n8n runs in Docker on a Cloud VPS that you control as root.
Prerequisites
- Root SSH access to the server running n8n.
- n8n running with Docker Compose (
docker composev2). - A second location for off-server copies, such as another VPS or your own machine.
- About 10 seconds of acceptable downtime per night if you use SQLite.
1. What exactly do you need to back up in n8n?
Start by finding your Compose directory, service names and data volume:
docker compose ls
cd /opt/n8n # use the directory shown in CONFIG FILES
docker compose ps
docker volume ls | grep -i n8n
This guide uses /opt/n8n, a service called n8n and a volume called n8n_n8n_data. Replace these with your own names.
Next, find out where your encryption key is stored. If .env contains N8N_ENCRYPTION_KEY, n8n uses that value. If it does not, n8n generated a key on first start and saved it in the data volume:
grep N8N_ENCRYPTION_KEY .env
docker compose exec n8n cat /home/node/.n8n/config
.env using the current value from the config file, and to make sure your Compose file passes it to the container. Never change the key on an existing instance.2. How do you export workflows and credentials with the n8n CLI?
CLI exports give you a second layer of protection. They are portable JSON files that you can import into any n8n instance:
docker compose exec -u node n8n n8n export:workflow --all --output=/tmp/workflows.json
docker compose exec -u node n8n n8n export:credentials --all --output=/tmp/credentials.json
docker compose cp n8n:/tmp/workflows.json ./
docker compose cp n8n:/tmp/credentials.json ./
The credentials export above stays encrypted, so it only works with the same key. To move credentials to an instance that uses a different key, add --decrypted:
docker compose exec -u node n8n n8n export:credentials --all --decrypted --output=/tmp/credentials-plain.json
docker compose exec n8n rm /tmp/credentials-plain.json.3. How do you create a nightly n8n backup script?
SQLite should not be copied while n8n is writing to it, so the script stops n8n briefly while it archives the volume. A Postgres database can be dumped safely while it runs, because pg_dump produces a consistent snapshot.
Create /usr/local/bin/n8n-backup.sh:
#!/usr/bin/env bash
set -euo pipefail
COMPOSE_DIR=/opt/n8n
VOLUME=n8n_n8n_data
BACKUP_DIR=/root/n8n-backups
USE_POSTGRES=no # set to yes if n8n uses Postgres
PG_USER=n8n
PG_DB=n8n
KEEP_DAYS=14
STAMP=$(date +%F_%H%M)
DEST="$BACKUP_DIR/$STAMP"
umask 077
mkdir -p "$DEST"
cd "$COMPOSE_DIR"
cp docker-compose.yml .env "$DEST/"
docker compose exec -T -u node n8n n8n export:workflow --all --output=/tmp/workflows.json
docker compose cp n8n:/tmp/workflows.json "$DEST/"
if [ "$USE_POSTGRES" = "yes" ]; then
docker compose exec -T postgres pg_dump -U "$PG_USER" -d "$PG_DB" -Fc > "$DEST/n8n.dump"
fi
docker compose stop n8n
trap 'docker compose start n8n' EXIT
docker run --rm -v "$VOLUME":/data:ro -v "$DEST":/backup alpine \
tar czf /backup/n8n_data.tar.gz -C /data .
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +"$KEEP_DAYS" -exec rm -rf {} +
echo "Backup finished: $DEST"
The trap line restarts n8n even if the archive step fails. Make the script executable, run it once by hand, then schedule it for 02:30 every night:
chmod 700 /usr/local/bin/n8n-backup.sh
/usr/local/bin/n8n-backup.sh
echo '30 2 * * * root /usr/local/bin/n8n-backup.sh >> /var/log/n8n-backup.log 2>&1' > /etc/cron.d/n8n-backup
Webhooks that arrive during the few seconds n8n is stopped will fail. Pick a quiet time for the schedule.
4. How do you copy n8n backups off the server?
A backup that lives only on the same server is lost along with that server. Push each backup to a second machine with rsync over SSH. First set up key-based login for root to the remote user, then add this line to the end of the script:
rsync -az "$BACKUP_DIR/" backupuser@203.0.113.20:/srv/backups/n8n/
A small Cloud VPS in a different region works well as a backup target. You can also pull backups down to your own machine:
scp -r root@203.0.113.10:/root/n8n-backups ./
5. How do you restore n8n on the same or a new server?
On a new server, install Docker first. Then copy one backup folder to /root/restore and put the Compose files in place:
mkdir -p /opt/n8n && cd /opt/n8n
cp /root/restore/docker-compose.yml /root/restore/.env ./
docker compose down # only if a stack is already running here
docker compose up --no-start
docker volume ls | grep -i n8n
The up --no-start command creates the volumes with the names Compose expects. Compose names volumes after the directory, so keeping /opt/n8n keeps the name n8n_n8n_data.
Next, unpack the data into the volume and give ownership to the container's node user (UID 1000):
docker run --rm -v n8n_n8n_data:/data -v /root/restore:/backup alpine \
sh -c "rm -rf /data/* /data/.[!.]* ; tar xzf /backup/n8n_data.tar.gz -C /data && chown -R 1000:1000 /data"
If you use Postgres, start only the database, load the dump, and then start everything:
docker compose up -d postgres
sleep 10
docker compose exec -T postgres pg_restore -U n8n -d n8n --clean --if-exists < /root/restore/n8n.dump
docker compose up -d
Keep these points in mind when restoring:
- Use the same n8n image version, or a newer one. Never restore into an older version.
- If the server's IP changed, update your DNS A record. Also check that
WEBHOOK_URLin.envis still correct. - If you only have the CLI exports, start a fresh n8n and import them:
docker compose cp workflows.json n8n:/tmp/workflows.json
docker compose cp credentials-plain.json n8n:/tmp/credentials.json
docker compose exec -u node n8n n8n import:workflow --input=/tmp/workflows.json
docker compose exec -u node n8n n8n import:credentials --input=/tmp/credentials.json
Verify it works
docker compose ps
docker compose logs --tail=50 n8n
Then check the restored instance in the browser:
- Log in with your existing owner account. You should not see the setup screen.
- Open a few credentials and confirm they load without decryption errors.
- Run one workflow manually.
Repeat this test restore on a spare VPS every few months. A backup you have never restored is untested.
Common problems
- Credentials show "could not be decrypted" after restore: the encryption key does not match the one the data was saved with. Put the original key back in
.envor in theconfigfile, then rundocker compose up -d --force-recreate n8n. - n8n will not start and logs "Mismatching encryption keys": the key in
/home/node/.n8n/configandN8N_ENCRYPTION_KEYare different. Set both to the original value. EACCES: permission deniedon/home/node/.n8n: the files are owned by root. Re-run thechown -R 1000:1000 /datastep against the volume.SQLITE_BUSY: database is locked: the backup was copied while n8n was running, or two containers share one volume. Stop n8n before archiving, and make sure only one n8n container is attached to the volume.- The setup screen appears and workflows are missing: the data went into a different volume than the one Compose mounts. Compare the output of
docker volume lswithdocker inspecton the n8n container, then restore into the correct volume.
Frequently asked questions
Can I recover n8n credentials without the encryption key?
No. Credentials are encrypted with that key, so without it you must re-enter every credential by hand. Workflows themselves can still be restored.
Is a VPS snapshot enough as an n8n backup?
A snapshot is useful, but it is not a replacement. File-level backups stored off-server let you restore onto any server, and let you recover a single workflow without rolling back the whole machine.
Do I need to stop n8n to back up Postgres?
No. pg_dump takes a consistent snapshot while the database is running. Stopping n8n is only needed for the SQLite file in the data volume.
Need help? If a restore fails, or you need rescue mode, an IP change or a plan upgrade, open a ticket at https://cloudesta.com/submitticket.php.