<p><img class="alignnone size-full wp-image-3719" src="https://flowgenius.in/wp-content/uploads/2025/12/Blog-12-Cluster-5.png" alt="" /></p>
<p style="text-align: center;">Step by Step Guide to Migrate n8n from SQLite to PostgreSQL</p>
<p> </p>
<p style="margin-bottom: 2em; line-height: 1.9;"><strong>Who this is for:</strong> n8n administrators who need a reliable, production‑grade PostgreSQL backend instead of the default SQLite store. For a complete guide on managing SQLite in n8n, including errors, performance, and migration, check out our <strong>n8n SQLite pillar guide</strong>.</p>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">Quick Diagnosis</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Step</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Command / Action</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Result</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">1</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>sqlite3 ~/.n8n/database.sqlite ".dump" > dump.sql</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Export full SQLite schema + data</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">2</td>
<td style="padding: 13px; border: 1px solid #ddd;">Edit <code>dump.sql</code> → replace <code>INTEGER PRIMARY KEY AUTOINCREMENT</code> with <code>SERIAL PRIMARY KEY</code> (or use <strong>pgloader</strong>)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Make the dump PostgreSQL‑compatible</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">3</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>createdb n8n</code><br />
<code>psql n8n < dump.sql</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Load schema & data into a fresh PostgreSQL DB</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">4</td>
<td style="padding: 13px; border: 1px solid #ddd;">Update <code>~/.n8n/.env</code> → <code>DB_TYPE=postgresdb</code><br />
<code>DB_POSTGRESDB_HOST=localhost</code><br />
<code>DB_POSTGRESDB_PORT=5432</code><br />
<code>DB_POSTGRESDB_DATABASE=n8n</code><br />
<code>DB_POSTGRESDB_USER=YOUR_USER</code><br />
<code>DB_POSTGRESDB_PASSWORD=YOUR_PASS</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Point n8n to PostgreSQL</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">5</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>npm run start</code> (or restart Docker)</td>
<td style="padding: 13px; border: 1px solid #ddd;">n8n boots with PostgreSQL backend</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">6</td>
<td style="padding: 13px; border: 1px solid #ddd;">Verify → <code>SELECT COUNT(*) FROM workflow_entity;</code> in psql</td>
<td style="padding: 13px; border: 1px solid #ddd;">All workflows are present</td>
</tr>
</tbody>
</table>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">One‑Minute Migration Checklist</h2>
<ol style="margin-bottom: 1.8em; line-height: 1.9;">
<li>Export SQLite DB.</li>
<li>Convert the dump for PostgreSQL (manual <code>sed</code> or <code>pgloader</code>).</li>
<li>Create a new PostgreSQL DB.</li>
<li>Import the converted dump.</li>
<li>Reconfigure n8n’s <code>.env</code>.</li>
<li>Restart n8n and run a quick <code>SELECT</code> test.</li>
</ol>
<p style="margin-bottom: 2em; line-height: 1.9;">If any step fails, consult the detailed sections below.<br />
<a href="https://flowgenius.in/how-to-enable-and-use-sqlite-logging-in-n8n-for-debugging/"><strong>Debug logging issues and monitor constraints</strong></a>, compatibility, and migration steps in SQLite for n8n.</p>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">1. Export the SQLite Database</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">1.1 Locate n8n’s SQLite file</h3>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Environment</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Default Path</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Linux/macOS (npm)</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>~/.n8n/database.sqlite</code></td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Docker (official image)</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>/home/node/.n8n/database.sqlite</code></td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Windows (npm)</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>%USERPROFILE%\.n8n\database.sqlite</code></td>
</tr>
</tbody>
</table>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd;"><p><strong>EEFA note:</strong> Stop the n8n process before exporting to avoid a partially‑written DB. In Docker, run <code>docker stop <container></code>.</p></blockquote>
<h3 style="margin-bottom: 45px; line-height: 1.3;">1.2 Dump the whole DB</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">sqlite3 "$HOME/.n8n/database.sqlite" ".dump" > /tmp/n8n_sqlite_dump.sql
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">Check the dump size – it should be roughly the same as the original file (± 10 KB).</p>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">2. Convert the SQLite Dump to PostgreSQL‑compatible SQL</h2>
<p style="margin-bottom: 2em; line-height: 1.9;">SQLite and PostgreSQL differ in data‑type syntax, autoincrement handling, and boolean literals.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.1 Quick‑and‑dirty manual conversion (small DB)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">sed -i \
-e 's/INTEGER PRIMARY KEY AUTOINCREMENT/SERIAL PRIMARY KEY/g' \
-e 's/TRUE/TRUE/g' \
-e 's/FALSE/FALSE/g' \
-e 's/`//g' \
/tmp/n8n_sqlite_dump.sql
</pre>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">SQLite syntax</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">PostgreSQL equivalent</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><code>INTEGER PRIMARY KEY AUTOINCREMENT</code></td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>SERIAL PRIMARY KEY</code></td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><code>TEXT</code></td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>VARCHAR</code> (or keep as <code>TEXT</code>)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;"><code>BOOLEAN</code> (stored as 0/1)</td>
<td style="padding: 13px; border: 1px solid #ddd;"><code>BOOLEAN</code> (use <code>TRUE/FALSE</code>)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Back‑ticks <code>`</code></td>
<td style="padding: 13px; border: 1px solid #ddd;">Double quotes <code>"</code> (or omit)</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd;"><p><strong>EEFA warning:</strong> Manual <code>sed</code> works for default n8n tables, but <strong>custom user tables</strong> may need bespoke adjustments. Review the modified dump before importing.</p></blockquote>
<h3 style="margin-bottom: 45px; line-height: 1.3;">2.2 Automated conversion with <strong>pgloader</strong> (recommended for > 10 k rows)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Install pgloader (Debian/Ubuntu)
sudo apt-get install pgloader
</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Run conversion
pgloader sqlite:///home/$(whoami)/.n8n/database.sqlite \
postgresql://YOUR_USER:YOUR_PASS@localhost/n8n
</pre>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">3. Create & Prepare the PostgreSQL Target Database</h2>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Create the database (requires superuser or createdb privilege)
createdb -U YOUR_USER n8n
</pre>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Parameter</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Recommended Setting</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">max_connections</td>
<td style="padding: 13px; border: 1px solid #ddd;">200 (if you expect many concurrent workflows)</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">shared_buffers</td>
<td style="padding: 13px; border: 1px solid #ddd;">25% of RAM</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">work_mem</td>
<td style="padding: 13px; border: 1px solid #ddd;">64MB (adjust per workflow complexity)</td>
</tr>
</tbody>
</table>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd;"><p><strong>EEFA tip:</strong> Enable <strong>pg_stat_statements</strong> and <strong>log_min_duration_statement</strong> for query‑performance monitoring after migration. Ensure <a href="https://flowgenius.in/n8n-sqlite-version-compatibility-which-sqlite-builds-work-with-each-n8n-release/"><strong>smooth operation by addressing version conflicts</strong></a>, unsupported features, and constraint errors in n8n SQLite.</p></blockquote>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">4. Import the Converted Dump (manual method)</h2>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">psql -U YOUR_USER -d n8n -f /tmp/n8n_sqlite_dump.sql
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">Verify import success</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">SELECT table_name, row_estimate
FROM pg_stat_user_tables
ORDER BY row_estimate DESC;
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">You should see tables such as <code>workflow_entity</code>, <code>execution_entity</code>, and <code>credential_entity</code> with row counts matching the original SQLite DB (run <code>SELECT COUNT(*) FROM workflow_entity;</code> in SQLite beforehand for comparison).</p>
<h2 style="margin-bottom: 45px; line-height: 1.3;">5. Reconfigure n8n to Use PostgreSQL</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">5.1 Update the <code>.env</code> file</h3>
<p style="margin-bottom: 2em; line-height: 1.9;">Replace the SQLite settings with PostgreSQL credentials:</p>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Existing SQLite config (remove or comment out)
# DB_TYPE=sqlite
# DB_SQLITE_DATABASE=/home/node/.n8n/database.sqlite
</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># New PostgreSQL config
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=localhost
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=YOUR_USER
DB_POSTGRESDB_PASSWORD=YOUR_PASS
# Optional: SSL mode
# DB_POSTGRESDB_SSL=true
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">5.2 Docker‑compose example (if you run n8n in Docker)</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">services:
n8n:
image: n8nio/n8n
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=strong_password
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=strong_password
- POSTGRES_DB=n8n
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
</pre>
<blockquote style="margin: 0 0 2em 0; padding-left: 1em; border-left: 4px solid #ddd;"><p><strong>EEFA note:</strong> In Docker, the <code>DB_POSTGRESDB_HOST</code> must match the service name (<code>db</code>). Set <code>restart: unless-stopped</code> for production stability.</p></blockquote>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">6. Restart n8n and Verify the Migration</h2>
<h3 style="margin-bottom: 45px; line-height: 1.3;">6.1 Start n8n</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># If installed via npm
npm run start
</pre>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;"># Docker
docker-compose up -d n8n
</pre>
<h3 style="margin-bottom: 45px; line-height: 1.3;">6.2 Quick sanity check</h3>
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">psql -U YOUR_USER -d n8n -c "SELECT COUNT(*) FROM workflow_entity;"
</pre>
<p style="margin-bottom: 2em; line-height: 1.9;">The count should equal the number of workflows you had in SQLite.</p>
<h3 style="margin-bottom: 45px; line-height: 1.3;">6.3 UI verification</h3>
<ol style="margin-bottom: 1.8em; line-height: 1.9;">
<li>Open the n8n web UI (<code>http://localhost:5678</code>).</li>
<li>Navigate to <strong>Workflows → All Workflows</strong>.</li>
<li>Confirm that every workflow loads without error.</li>
</ol>
<p style="margin-bottom: 2em; line-height: 1.9;">If you see “Failed to load workflow” errors, consult the troubleshooting checklist below. Understand <a href="https://flowgenius.in/n8n-sqlite-foreignkey-constraint-failure-diagnostic-guide/"><strong>foreign key constraints</strong></a>, version compatibility, and unsupported feature issues in n8n SQLite.</p>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">7. Troubleshooting Checklist</h2>
<table style="border-collapse: collapse; width: 100%; margin-bottom: 2em;">
<thead>
<tr>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Symptom</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Likely Cause</th>
<th style="padding: 13px; border: 1px solid #ddd; text-align: left;">Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">ERROR: column “id” of relation “workflow_entity” does not exist</td>
<td style="padding: 13px; border: 1px solid #ddd;">Dump still contains AUTOINCREMENT syntax</td>
<td style="padding: 13px; border: 1px solid #ddd;">Re‑run sed replacement or use pgloader.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">psql: FATAL: password authentication failed for user “n8n”</td>
<td style="padding: 13px; border: 1px solid #ddd;">Wrong credentials in .env</td>
<td style="padding: 13px; border: 1px solid #ddd;">Verify DB_POSTGRESDB_USER / PASSWORD match PostgreSQL role.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">ERROR: duplicate key value violates unique constraint “workflow_entity_pkey”</td>
<td style="padding: 13px; border: 1px solid #ddd;">Duplicate rows imported (e.g., ran dump twice)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Drop the DB and re‑import cleanly.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">n8n throws “Failed to connect to DB” on start</td>
<td style="padding: 13px; border: 1px solid #ddd;">PostgreSQL not reachable (Docker network, firewall)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Test connectivity: <code>nc -zv localhost 5432</code>.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Missing workflow data after migration</td>
<td style="padding: 13px; border: 1px solid #ddd;">Incomplete dump (e.g., .dump interrupted)</td>
<td style="padding: 13px; border: 1px solid #ddd;">Re‑export SQLite after stopping n8n.</td>
</tr>
<tr>
<td style="padding: 13px; border: 1px solid #ddd;">Performance slowdown after migration</td>
<td style="padding: 13px; border: 1px solid #ddd;">PostgreSQL default shared_buffers too low</td>
<td style="padding: 13px; border: 1px solid #ddd;">Increase shared_buffers to 25 % of RAM, restart PostgreSQL.</td>
</tr>
</tbody>
</table>
<hr style="margin: 50px 0;" />
<h2 style="margin-bottom: 45px; line-height: 1.3;">8. Post‑Migration Best Practices</h2>
<ol style="margin-bottom: 1.8em; line-height: 1.9;">
<li><strong>Backup</strong> the new PostgreSQL DB daily:
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">pg_dump -Fc -U YOUR_USER -d n8n > backup_$(date +%F).dump
</pre>
</li>
<li>Enable <strong>logical replication</strong> for zero‑downtime upgrades.</li>
<li>Turn on n8n’s DB health checks:
<pre style="background: #fafafa; padding: 20px; border: 1px solid #e0e0e0; overflow: auto;">N8N_DISABLE_DATABASE_HEALTH_CHECK=false
</pre>
</li>
<li>If using a managed Postgres service, set <code>DB_POSTGRESDB_SSL=true</code> and provide the appropriate CA bundle.</li>
</ol>
<hr style="margin: 50px 0;" />
<p style="margin-bottom: 2em; line-height: 1.9;"><em>This guide follows production‑grade practices (EEFA) and assumes you have PostgreSQL 12+ installed. Adjust version‑specific flags accordingly.</em></p>

Step by Step Guide to Migrate n8n from SQLite to PostgreSQL
Who this is for: n8n administrators who need a reliable, production‑grade PostgreSQL backend instead of the default SQLite store. For a complete guide on managing SQLite in n8n, including errors, performance, and migration, check out our n8n SQLite pillar guide.
Quick Diagnosis
| Step |
Command / Action |
Result |
| 1 |
sqlite3 ~/.n8n/database.sqlite ".dump" > dump.sql |
Export full SQLite schema + data |
| 2 |
Edit dump.sql → replace INTEGER PRIMARY KEY AUTOINCREMENT with SERIAL PRIMARY KEY (or use pgloader) |
Make the dump PostgreSQL‑compatible |
| 3 |
createdb n8n
psql n8n < dump.sql |
Load schema & data into a fresh PostgreSQL DB |
| 4 |
Update ~/.n8n/.env → DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=localhost
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=YOUR_USER
DB_POSTGRESDB_PASSWORD=YOUR_PASS |
Point n8n to PostgreSQL |
| 5 |
npm run start (or restart Docker) |
n8n boots with PostgreSQL backend |
| 6 |
Verify → SELECT COUNT(*) FROM workflow_entity; in psql |
All workflows are present |
One‑Minute Migration Checklist
- Export SQLite DB.
- Convert the dump for PostgreSQL (manual
sed or pgloader).
- Create a new PostgreSQL DB.
- Import the converted dump.
- Reconfigure n8n’s
.env.
- Restart n8n and run a quick
SELECT test.
If any step fails, consult the detailed sections below.
Debug logging issues and monitor constraints, compatibility, and migration steps in SQLite for n8n.
1. Export the SQLite Database
1.1 Locate n8n’s SQLite file
| Environment |
Default Path |
| Linux/macOS (npm) |
~/.n8n/database.sqlite |
| Docker (official image) |
/home/node/.n8n/database.sqlite |
| Windows (npm) |
%USERPROFILE%\.n8n\database.sqlite |
EEFA note: Stop the n8n process before exporting to avoid a partially‑written DB. In Docker, run docker stop <container>.
1.2 Dump the whole DB
sqlite3 "$HOME/.n8n/database.sqlite" ".dump" > /tmp/n8n_sqlite_dump.sql
Check the dump size – it should be roughly the same as the original file (± 10 KB).
2. Convert the SQLite Dump to PostgreSQL‑compatible SQL
SQLite and PostgreSQL differ in data‑type syntax, autoincrement handling, and boolean literals.
2.1 Quick‑and‑dirty manual conversion (small DB)
sed -i \
-e 's/INTEGER PRIMARY KEY AUTOINCREMENT/SERIAL PRIMARY KEY/g' \
-e 's/TRUE/TRUE/g' \
-e 's/FALSE/FALSE/g' \
-e 's/`//g' \
/tmp/n8n_sqlite_dump.sql
| SQLite syntax |
PostgreSQL equivalent |
INTEGER PRIMARY KEY AUTOINCREMENT |
SERIAL PRIMARY KEY |
TEXT |
VARCHAR (or keep as TEXT) |
BOOLEAN (stored as 0/1) |
BOOLEAN (use TRUE/FALSE) |
Back‑ticks ` |
Double quotes " (or omit) |
EEFA warning: Manual sed works for default n8n tables, but custom user tables may need bespoke adjustments. Review the modified dump before importing.
2.2 Automated conversion with pgloader (recommended for > 10 k rows)
# Install pgloader (Debian/Ubuntu)
sudo apt-get install pgloader
# Run conversion
pgloader sqlite:///home/$(whoami)/.n8n/database.sqlite \
postgresql://YOUR_USER:YOUR_PASS@localhost/n8n
3. Create & Prepare the PostgreSQL Target Database
# Create the database (requires superuser or createdb privilege)
createdb -U YOUR_USER n8n
| Parameter |
Recommended Setting |
| max_connections |
200 (if you expect many concurrent workflows) |
| shared_buffers |
25% of RAM |
| work_mem |
64MB (adjust per workflow complexity) |
EEFA tip: Enable pg_stat_statements and log_min_duration_statement for query‑performance monitoring after migration. Ensure smooth operation by addressing version conflicts, unsupported features, and constraint errors in n8n SQLite.
4. Import the Converted Dump (manual method)
psql -U YOUR_USER -d n8n -f /tmp/n8n_sqlite_dump.sql
Verify import success
SELECT table_name, row_estimate
FROM pg_stat_user_tables
ORDER BY row_estimate DESC;
You should see tables such as workflow_entity, execution_entity, and credential_entity with row counts matching the original SQLite DB (run SELECT COUNT(*) FROM workflow_entity; in SQLite beforehand for comparison).
5. Reconfigure n8n to Use PostgreSQL
5.1 Update the .env file
Replace the SQLite settings with PostgreSQL credentials:
# Existing SQLite config (remove or comment out)
# DB_TYPE=sqlite
# DB_SQLITE_DATABASE=/home/node/.n8n/database.sqlite
# New PostgreSQL config
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=localhost
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=YOUR_USER
DB_POSTGRESDB_PASSWORD=YOUR_PASS
# Optional: SSL mode
# DB_POSTGRESDB_SSL=true
5.2 Docker‑compose example (if you run n8n in Docker)
services:
n8n:
image: n8nio/n8n
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=db
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=strong_password
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=strong_password
- POSTGRES_DB=n8n
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
EEFA note: In Docker, the DB_POSTGRESDB_HOST must match the service name (db). Set restart: unless-stopped for production stability.
6. Restart n8n and Verify the Migration
6.1 Start n8n
# If installed via npm
npm run start
# Docker
docker-compose up -d n8n
6.2 Quick sanity check
psql -U YOUR_USER -d n8n -c "SELECT COUNT(*) FROM workflow_entity;"
The count should equal the number of workflows you had in SQLite.
6.3 UI verification
- Open the n8n web UI (
http://localhost:5678).
- Navigate to Workflows → All Workflows.
- Confirm that every workflow loads without error.
If you see “Failed to load workflow” errors, consult the troubleshooting checklist below. Understand foreign key constraints, version compatibility, and unsupported feature issues in n8n SQLite.
7. Troubleshooting Checklist
| Symptom |
Likely Cause |
Fix |
| ERROR: column “id” of relation “workflow_entity” does not exist |
Dump still contains AUTOINCREMENT syntax |
Re‑run sed replacement or use pgloader. |
| psql: FATAL: password authentication failed for user “n8n” |
Wrong credentials in .env |
Verify DB_POSTGRESDB_USER / PASSWORD match PostgreSQL role. |
| ERROR: duplicate key value violates unique constraint “workflow_entity_pkey” |
Duplicate rows imported (e.g., ran dump twice) |
Drop the DB and re‑import cleanly. |
| n8n throws “Failed to connect to DB” on start |
PostgreSQL not reachable (Docker network, firewall) |
Test connectivity: nc -zv localhost 5432. |
| Missing workflow data after migration |
Incomplete dump (e.g., .dump interrupted) |
Re‑export SQLite after stopping n8n. |
| Performance slowdown after migration |
PostgreSQL default shared_buffers too low |
Increase shared_buffers to 25 % of RAM, restart PostgreSQL. |
8. Post‑Migration Best Practices
- Backup the new PostgreSQL DB daily:
pg_dump -Fc -U YOUR_USER -d n8n > backup_$(date +%F).dump
- Enable logical replication for zero‑downtime upgrades.
- Turn on n8n’s DB health checks:
N8N_DISABLE_DATABASE_HEALTH_CHECK=false
- If using a managed Postgres service, set
DB_POSTGRESDB_SSL=true and provide the appropriate CA bundle.
This guide follows production‑grade practices (EEFA) and assumes you have PostgreSQL 12+ installed. Adjust version‑specific flags accordingly.