<h2><img class="alignnone size-full wp-image-3717" src="https://flowgenius.in/wp-content/uploads/2025/12/Blog-10-Cluster-5.png" alt="" /></h2>
<p style="text-align: center;">Step by Step Guide Backup and Restore the n8n SQLite Database</p>
<p> </p>
<p>For a complete guide on managing SQLite in n8n, including errors, performance, and migration, check out our <strong>n8n SQLite pillar guide</strong>.</p>
<p> </p>
<h2 style="margin: 0;">Quick Diagnosis</h2>
<ol style="margin: 0; padding-left: 1.2em;">
<li><strong>Stop n8n</strong> – <code>docker stop n8n</code> or <code>pm2 stop n8n</code>.</li>
<li><strong>Copy the DB file</strong> – <code>cp ~/.n8n/.n8n.sqlite ./backup/n8n-$(date +%F).sqlite</code>.</li>
<li><strong>Start n8n</strong> – <code>docker start n8n</code> or <code>pm2 start n8n</code>.</li>
<li><strong>To restore</strong>, stop n8n, replace the file with the backup, then start n8n again.</li>
<li>Verify with <code>n8n start --tunnel</code> and check the UI; the workflow list should match the backup.</li>
</ol>
<hr style="margin: 55px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Why a Dedicated Backup/Restore Guide?</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">The n8n SQLite error overview explains common failure modes, but backing up the database safely is a separate operational task. A corrupted or lost <code>.sqlite</code> file means lost workflows, credentials, and execution history. This guide walks you through <strong>production‑grade</strong> backup and restore procedures that avoid race conditions, preserve file permissions, and work whether you run n8n in Docker, via PM2, or as a system service. Find out how to manage <a href="https://flowgenius.in/n8n-sqlite-disk-full-error/"><strong>disk space in n8n</strong></a>, prevent corruption, and optimize SQLite performance in n8n.</p>
<hr style="margin: 55px 0;" />
<h2 id="prerequisites" style="margin-bottom: 45px; line-height: 1.3;">Prerequisites</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd;">Requirement</th>
<th style="padding: 13px; border: 1px solid #ddd;">Detail</th>
<th style="padding: 13px; border: 1px solid #ddd;">Why it matters</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">**n8n version**</td>
<td style="padding: 13px; border: 1px solid #ddd;">≥ 1.0 (SQLite is still the default storage)</td>
<td style="padding: 13px; border: 1px solid #ddd;">API and CLI flags differ before v1.0</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">**Access rights**</td>
<td style="padding: 13px; border: 1px solid #ddd;">OS user that runs n8n must own <code>~/.n8n/.n8n.sqlite</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Prevents “permission denied” errors during copy</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">**Backup destination**</td>
<td style="padding: 13px; border: 1px solid #ddd;">Separate filesystem or remote bucket (e.g., S3, GCS)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Guarantees durability beyond a single host failure</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">**Optional**</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>sqlite3</code> binary installed (<code>apt install sqlite3</code> or <code>brew install sqlite3</code>)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Enables hot‑backup via SQLite’s own API</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA note:</strong> Never back up the DB while n8n is writing to it *unless* you use SQLite’s <strong>online backup API</strong> (<code>.backup</code>). A live copy can lead to a corrupted backup file.</p>
<hr style="margin: 55px 0;" />
<h2 id="backup-strategies" style="margin-bottom: 45px; line-height: 1.3;">Backup Strategies</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.1 Manual File Copy (Offline)</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Best for small self‑hosted installations or one‑off backups where a brief downtime is acceptable.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Stop n8n (Docker example)
docker stop n8n
# Create a timestamped backup folder
mkdir -p /var/backups/n8n
BACKUP=/var/backups/n8n/n8n-$(date +%F-%H%M).sqlite
# Copy the DB file
cp ~/.n8n/.n8n.sqlite "$BACKUP"
# Optional: Verify checksum
sha256sum "$BACKUP" > "$BACKUP.sha256"
# Restart n8n
docker start n8n
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.2 Hot Backup with <code>sqlite3</code> (Online)</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Use when downtime is not an option. SQLite’s <code>.backup</code> command creates a consistent snapshot while the database remains active.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Run as the same user that owns the DB file
sqlite3 ~/.n8n/.n8n.sqlite \
".backup '/var/backups/n8n/n8n-$(date +%F-%H%M).sqlite'"
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>EEFA Warning</strong> – If the underlying filesystem does **not** support atomic rename (e.g., async NFS), the backup may still be inconsistent. Prefer a local ext4/xfs volume or a cloud block store with strong consistency guarantees.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.3 Docker‑Native Volume Snapshot</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">For deployments that use a named Docker volume (<code>n8n-data</code>). This method copies the DB from a temporary container, avoiding any impact on the running n8n instance. Learn <a href="https://flowgenius.in/how-to-backup-and-restore-the-n8n-sqlite-database-stepbystep-guide/"><strong>recovery steps for a corrupt SQLite database</strong></a> and other storage-related issues in n8n.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">docker run --rm \
-v n8n-data:/data \
-v $(pwd)/backup:/backup \
alpine \
sh -c "cp /data/.n8n.sqlite /backup/n8n-$(date +%F-%H%M).sqlite"
</pre>
<hr style="margin: 55px 0;" />
<h2 id="verify-the-backup" style="margin-bottom: 45px; line-height: 1.3;">Verify the Backup</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd;">Check</th>
<th style="padding: 13px; border: 1px solid #ddd;">Command</th>
<th style="padding: 13px; border: 1px solid #ddd;">Expected outcome</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">File size</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>stat -c %s backup.sqlite</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Matches the live DB size (± few KB)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">SQLite integrity</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>sqlite3 backup.sqlite "PRAGMA integrity_check;"</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Returns <code>ok</code></td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Workflow count</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>sqlite3 backup.sqlite "SELECT COUNT(*) FROM workflow_entity;"</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Same as live DB (run before/after)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Checksum</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>sha256sum backup.sqlite</code> vs stored checksum</td>
<td style="padding: 13px; border: 1px solid #ddd;">Identical hash</td>
</tr>
</tbody>
</table>
<p style="margin-bottom: 2em; line-height: 1.9;">If any check fails, repeat the backup using the **online method** (2.2) to avoid a partially‑written file.</p>
<hr style="margin: 55px 0;" />
<h2 id="restore-procedures" style="margin-bottom: 45px; line-height: 1.3;">Restore Procedures</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.1 Manual Restore (Same Host)</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Replace the corrupted or lost DB with a known good backup on the original host.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Stop n8n
docker stop n8n # or pm2 stop n8n
# Replace the DB file
cp /var/backups/n8n/n8n-2025-12-23.sqlite ~/.n8n/.n8n.sqlite
# Ensure correct ownership (Docker runs as root by default)
chown 1000:1000 ~/.n8n/.n8n.sqlite # adjust UID/GID as needed
# Restart n8n
docker start n8n
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Post‑restore sanity check</strong> – Open the n8n UI, go to *Workflows*, and verify that the expected number of workflows appears. <a href="https://flowgenius.in/n8n-sqlite-performance-tuning-indexing-pragma-settings-amp-workflow-design-tips/"><strong>Optimize performance and prevent storage issues</strong></a> like corruption and disk full errors in n8n SQLite.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">4.2 Restoring into a New n8n Instance (Migration)</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Use this when moving to a fresh host or rebuilding the container.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Create a fresh n8n container with an empty volume
docker compose up -d n8n
# Copy the backup into the new volume before the first start
docker run --rm \
-v new-n8n-data:/data \
-v $(pwd)/backup:/backup \
alpine \
sh -c "cp /backup/n8n-2025-12-23.sqlite /data/.n8n.sqlite && \
chown 1000:1000 /data/.n8n.sqlite"
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">After the copy, start the container (<code>docker compose up -d n8n</code>) and verify the UI as above.</p>
<hr style="margin: 55px 0;" />
<h2 id="automating-daily-backups" style="margin-bottom: 45px; line-height: 1.3;">Automating Daily Backups</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">A nightly cron job (or systemd timer) can run the **online backup** during low‑traffic hours.</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># /etc/cron.d/n8n-sqlite-backup
30 2 * * * n8nuser /usr/bin/sqlite3 /home/n8n/.n8n/.n8n.sqlite \
".backup '/var/backups/n8n/n8n-$(date +\%F-\%H\%M).sqlite'" \
&& sha256sum /var/backups/n8n/n8n-$(date +\%F-\%H\%M).sqlite \
> /var/backups/n8n/n8n-$(date +\%F-\%H\%M).sha256
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Rotation policy</strong> – Keep 30 daily, 12 weekly, and 6 monthly backups. A simple <code>find</code> command purges old files:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">find /var/backups/n8n -type f -name 'n8n-*.sqlite' -mtime +30 -delete
</pre>
<hr style="margin: 55px 0;" />
<h2 id="troubleshooting-common-issues" style="margin-bottom: 45px; line-height: 1.3;">Troubleshooting Common Issues</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd;">Symptom</th>
<th style="padding: 13px; border: 1px solid #ddd;">Likely cause</th>
<th style="padding: 13px; border: 1px solid #ddd;">Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">“SQLITE_BUSY: database is locked” during backup</td>
<td style="padding: 13px; border: 1px solid #ddd;">Using manual <code>cp</code> while n8n is running</td>
<td style="padding: 13px; border: 1px solid #ddd;">Switch to the **online <code>.backup</code>** method (2.2) or stop n8n briefly.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Restored DB shows 0 workflows</td>
<td style="padding: 13px; border: 1px solid #ddd;">Restored file is empty or corrupted</td>
<td style="padding: 13px; border: 1px solid #ddd;">Verify integrity with <code>PRAGMA integrity_check;</code>. Re‑run backup using a fresh snapshot.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Permission denied when copying</td>
<td style="padding: 13px; border: 1px solid #ddd;">Docker volume owned by root, host user is non‑root</td>
<td style="padding: 13px; border: 1px solid #ddd;">Use <code>docker exec</code> with <code>--user root</code> or adjust <code>chown</code> after copy.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Checksum mismatch after transfer to remote storage</td>
<td style="padding: 13px; border: 1px solid #ddd;">Transfer was interrupted (e.g., S3 multipart)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Enable MD5 verification on the upload client; re‑upload if mismatch.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">n8n fails to start after restore</td>
<td style="padding: 13px; border: 1px solid #ddd;">SQLite version mismatch (backup from SQLite 3.41, runtime uses 3.34)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Ensure the runtime includes the same SQLite library version, or export/import via <code>.dump</code>/<code>.read</code>.</td>
</tr>
</tbody>
</table>
<hr style="margin: 55px 0;" />
<h2 id="best-practice-checklist" style="margin-bottom: 45px; line-height: 1.3;">Best‑Practice Checklist</h2>
<ul style="margin-bottom: 1.8em; line-height: 1.9;">
<li>[ ] <strong>Stop n8n</strong> before any *offline* copy.</li>
<li>[ ] Use **SQLite’s <code>.backup</code> API** for hot backups whenever possible.</li>
<li>[ ] Store backups **off‑host** (S3, GCS, Azure Blob) with versioning enabled.</li>
<li>[ ] Verify each backup with <code>PRAGMA integrity_check;</code> and a checksum.</li>
<li>[ ] Automate retention with a **rotation policy** (30‑12‑6).</li>
<li>[ ] Test **restore** at least quarterly in a staging environment.</li>
<li>[ ] Document the backup schedule and location in your **run‑book**.</li>
</ul>

Step by Step Guide Backup and Restore the n8n SQLite Database
For a complete guide on managing SQLite in n8n, including errors, performance, and migration, check out our n8n SQLite pillar guide.
Quick Diagnosis
- Stop n8n –
docker stop n8n or pm2 stop n8n.
- Copy the DB file –
cp ~/.n8n/.n8n.sqlite ./backup/n8n-$(date +%F).sqlite.
- Start n8n –
docker start n8n or pm2 start n8n.
- To restore, stop n8n, replace the file with the backup, then start n8n again.
- Verify with
n8n start --tunnel and check the UI; the workflow list should match the backup.
Why a Dedicated Backup/Restore Guide?
The n8n SQLite error overview explains common failure modes, but backing up the database safely is a separate operational task. A corrupted or lost .sqlite file means lost workflows, credentials, and execution history. This guide walks you through production‑grade backup and restore procedures that avoid race conditions, preserve file permissions, and work whether you run n8n in Docker, via PM2, or as a system service. Find out how to manage disk space in n8n, prevent corruption, and optimize SQLite performance in n8n.
Prerequisites
| Requirement |
Detail |
Why it matters |
| **n8n version** |
≥ 1.0 (SQLite is still the default storage) |
API and CLI flags differ before v1.0 |
| **Access rights** |
OS user that runs n8n must own ~/.n8n/.n8n.sqlite |
Prevents “permission denied” errors during copy |
| **Backup destination** |
Separate filesystem or remote bucket (e.g., S3, GCS) |
Guarantees durability beyond a single host failure |
| **Optional** |
sqlite3 binary installed (apt install sqlite3 or brew install sqlite3) |
Enables hot‑backup via SQLite’s own API |
EEFA note: Never back up the DB while n8n is writing to it *unless* you use SQLite’s online backup API (.backup). A live copy can lead to a corrupted backup file.
Backup Strategies
2.1 Manual File Copy (Offline)
Best for small self‑hosted installations or one‑off backups where a brief downtime is acceptable.
# Stop n8n (Docker example)
docker stop n8n
# Create a timestamped backup folder
mkdir -p /var/backups/n8n
BACKUP=/var/backups/n8n/n8n-$(date +%F-%H%M).sqlite
# Copy the DB file
cp ~/.n8n/.n8n.sqlite "$BACKUP"
# Optional: Verify checksum
sha256sum "$BACKUP" > "$BACKUP.sha256"
# Restart n8n
docker start n8n
2.2 Hot Backup with sqlite3 (Online)
Use when downtime is not an option. SQLite’s .backup command creates a consistent snapshot while the database remains active.
# Run as the same user that owns the DB file
sqlite3 ~/.n8n/.n8n.sqlite \
".backup '/var/backups/n8n/n8n-$(date +%F-%H%M).sqlite'"
EEFA Warning – If the underlying filesystem does **not** support atomic rename (e.g., async NFS), the backup may still be inconsistent. Prefer a local ext4/xfs volume or a cloud block store with strong consistency guarantees.
2.3 Docker‑Native Volume Snapshot
For deployments that use a named Docker volume (n8n-data). This method copies the DB from a temporary container, avoiding any impact on the running n8n instance. Learn recovery steps for a corrupt SQLite database and other storage-related issues in n8n.
docker run --rm \
-v n8n-data:/data \
-v $(pwd)/backup:/backup \
alpine \
sh -c "cp /data/.n8n.sqlite /backup/n8n-$(date +%F-%H%M).sqlite"
Verify the Backup
| Check |
Command |
Expected outcome |
| File size |
stat -c %s backup.sqlite |
Matches the live DB size (± few KB) |
| SQLite integrity |
sqlite3 backup.sqlite "PRAGMA integrity_check;" |
Returns ok |
| Workflow count |
sqlite3 backup.sqlite "SELECT COUNT(*) FROM workflow_entity;" |
Same as live DB (run before/after) |
| Checksum |
sha256sum backup.sqlite vs stored checksum |
Identical hash |
If any check fails, repeat the backup using the **online method** (2.2) to avoid a partially‑written file.
Restore Procedures
4.1 Manual Restore (Same Host)
Replace the corrupted or lost DB with a known good backup on the original host.
# Stop n8n
docker stop n8n # or pm2 stop n8n
# Replace the DB file
cp /var/backups/n8n/n8n-2025-12-23.sqlite ~/.n8n/.n8n.sqlite
# Ensure correct ownership (Docker runs as root by default)
chown 1000:1000 ~/.n8n/.n8n.sqlite # adjust UID/GID as needed
# Restart n8n
docker start n8n
Post‑restore sanity check – Open the n8n UI, go to *Workflows*, and verify that the expected number of workflows appears. Optimize performance and prevent storage issues like corruption and disk full errors in n8n SQLite.
4.2 Restoring into a New n8n Instance (Migration)
Use this when moving to a fresh host or rebuilding the container.
# Create a fresh n8n container with an empty volume
docker compose up -d n8n
# Copy the backup into the new volume before the first start
docker run --rm \
-v new-n8n-data:/data \
-v $(pwd)/backup:/backup \
alpine \
sh -c "cp /backup/n8n-2025-12-23.sqlite /data/.n8n.sqlite && \
chown 1000:1000 /data/.n8n.sqlite"
After the copy, start the container (docker compose up -d n8n) and verify the UI as above.
Automating Daily Backups
A nightly cron job (or systemd timer) can run the **online backup** during low‑traffic hours.
# /etc/cron.d/n8n-sqlite-backup
30 2 * * * n8nuser /usr/bin/sqlite3 /home/n8n/.n8n/.n8n.sqlite \
".backup '/var/backups/n8n/n8n-$(date +\%F-\%H\%M).sqlite'" \
&& sha256sum /var/backups/n8n/n8n-$(date +\%F-\%H\%M).sqlite \
> /var/backups/n8n/n8n-$(date +\%F-\%H\%M).sha256
Rotation policy – Keep 30 daily, 12 weekly, and 6 monthly backups. A simple find command purges old files:
find /var/backups/n8n -type f -name 'n8n-*.sqlite' -mtime +30 -delete
Troubleshooting Common Issues
| Symptom |
Likely cause |
Fix |
| “SQLITE_BUSY: database is locked” during backup |
Using manual cp while n8n is running |
Switch to the **online .backup** method (2.2) or stop n8n briefly. |
| Restored DB shows 0 workflows |
Restored file is empty or corrupted |
Verify integrity with PRAGMA integrity_check;. Re‑run backup using a fresh snapshot. |
| Permission denied when copying |
Docker volume owned by root, host user is non‑root |
Use docker exec with --user root or adjust chown after copy. |
| Checksum mismatch after transfer to remote storage |
Transfer was interrupted (e.g., S3 multipart) |
Enable MD5 verification on the upload client; re‑upload if mismatch. |
| n8n fails to start after restore |
SQLite version mismatch (backup from SQLite 3.41, runtime uses 3.34) |
Ensure the runtime includes the same SQLite library version, or export/import via .dump/.read. |
Best‑Practice Checklist
- [ ] Stop n8n before any *offline* copy.
- [ ] Use **SQLite’s
.backup API** for hot backups whenever possible.
- [ ] Store backups **off‑host** (S3, GCS, Azure Blob) with versioning enabled.
- [ ] Verify each backup with
PRAGMA integrity_check; and a checksum.
- [ ] Automate retention with a **rotation policy** (30‑12‑6).
- [ ] Test **restore** at least quarterly in a staging environment.
- [ ] Document the backup schedule and location in your **run‑book**.