n8n ships with SQLite as its default database — a serverless,
file‑based engine that stores everything in a single database.sqlite
file within the .n8n directory. It requires zero configuration and is
perfect for local development, testing, and single‑user instances handling fewer
than 50 executions per day. PostgreSQL is the
recommended production database: it supports row‑level locking (MVCC), concurrent
writes from multiple worker processes, hot backups via pg_dump, and is
mandatory for queue mode — SQLite cannot safely handle the
multi‑process writes that queue mode generates and will corrupt under that load.
This guide provides the definitive configuration, performance benchmark, migration,
and data‑pruning reference for selecting and managing n8n’s database backend
[1]
[2].
| Dimension | SQLite | PostgreSQL |
|---|---|---|
| Default | ✅ Yes — zero config, file‑based [1] | ❌ No — requires separate server [2] |
| Queue Mode | ❌ Not supported — will corrupt [3] | ✅ Required [3] |
| Concurrency Model | Single writer — file‑level lock [6] | High — MVCC + row‑level locks [6] |
| Ideal Scale | <50 execs/day, single user [1] | 200+ execs/day, multi‑user, multi‑worker [3] |
| Hot Backups | ❌ Inconsistent copy while running [6] | ✅ pg_dump without downtime [6] |
| Disk Reclamation | VACUUM required; space reused, not freed [1] | Automatic via autovacuum [6] |
| WAL Mode | ✅ Set DB_SQLITE_POOL_SIZE > 0 [1] |
N/A (built‑in WAL) [6] |
Why is PostgreSQL mandatory for n8n queue mode and multi-worker deployments?
Queue mode splits n8n into a main process (UI, API, trigger listening) and multiple worker processes — each worker independently reads the database to fetch workflow definitions and writes execution results back concurrently. This architecture creates simultaneous multi-process database access, which SQLite cannot safely handle: SQLite uses a file‑level write lock, meaning only one worker can write at a time while others queue, and under sustained concurrent writes the database file will corrupt. [3] [6]
PostgreSQL uses MVCC (Multi-Version Concurrency Control) with row‑level locking, allowing multiple workers to read and write simultaneously without blocking each other. Beyond queue mode, PostgreSQL is also required for multi-main high availability setups (Enterprise feature) — running multiple main n8n instances that share a single database. Migrating a production n8n instance from SQLite to PostgreSQL once you outgrow single‑process operation is documented step‑by‑step in this guide (see the Migration section) and the official n8n database configuration reference [4]. For the complete queue mode architecture covering Redis, worker configuration, and scaling strategies, see the n8n Scaling & Queue Configuration guide.
DB_TYPE=postgresdb before enabling
EXECUTIONS_MODE=queue. If n8n falls back to SQLite despite
PostgreSQL configuration, verify that DB_TYPE is spelled
exactly postgresdb — any other value silently falls back.
[3]
What is the real-world performance difference between SQLite and PostgreSQL in n8n?
A benchmark test using two identical n8n v1.65.0 instances on a 2‑core 4 GB VPS — one running SQLite and the other PostgreSQL 15 — simulated a common 5‑node webhook‑triggered workflow. Three test scenarios reveal where the performance gap emerges: at 5 requests/second, SQLite averages ~210 ms and PostgreSQL ~205 ms — a negligible difference explaining why personal users rarely notice issues. At 20 requests/second, SQLite spikes to 1,200 ms with a 3% failure rate, while PostgreSQL remains steady at 280 ms with 0% failures. [7] [6]
A third test pre‑loaded 100,000 execution history records into both databases. SQLite’s query planner degraded significantly — the workflow list page load time exceeded 5 seconds, while PostgreSQL stayed under 1 second. The root cause is architectural: SQLite’s file‑level write lock serializes all writes, while PostgreSQL’s MVCC enables concurrent access. SQLite is measurably faster for read‑heavy workloads where reads outnumber writes 100:1 — there is no network round‑trip, and data is accessed locally. But in n8n production, writes (execution logs, progress updates) and reads (editor loading, workflow fetching) interleave constantly, and the write serialization becomes the bottleneck. For the complete production deployment guide, see the n8n Docker Compose production stack guide.
How do you configure n8n to use PostgreSQL via the DB_TYPE environment variable?
Set DB_TYPE=postgresdb — the value must be
exactly postgresdb. Using "postgres" or
"postgresql" causes n8n to silently fall back to
SQLite without any error. This is the single most common
misconfiguration. Then configure the six DB_POSTGRESDB_
variables: DB_POSTGRESDB_HOST,
DB_POSTGRESDB_PORT (default 5432),
DB_POSTGRESDB_DATABASE (default n8n),
DB_POSTGRESDB_USER, and
DB_POSTGRESDB_PASSWORD.
[4]
[2]
For SSL-encrypted connections, set
DB_POSTGRESDB_SSL_ENABLED=true along with optional
DB_POSTGRESDB_SSL_CA, DB_POSTGRESDB_SSL_CERT,
and DB_POSTGRESDB_SSL_KEY. For connection pooling,
configure DB_POSTGRESDB_POOL_SIZE (default: 2). Increase to
10‑20 for single‑instance production, and 20‑50 for queue mode with
multiple workers. For SQLite, no configuration is
required — n8n defaults to SQLite and stores the database file at
~/.n8n/database.sqlite. To enable WAL mode
for better SQLite concurrency, set DB_SQLITE_POOL_SIZE to a
value greater than zero (e.g., 4); this allows concurrent
reads with a single writer. For the complete database environment
variables reference, see the
n8n Credential Nodes guide.
| Variable | Required | Default | Example Production Value |
|---|---|---|---|
DB_TYPE |
✅ | sqlite |
postgresdb |
DB_POSTGRESDB_HOST |
✅ | localhost |
postgres (Docker service name) |
DB_POSTGRESDB_PORT |
❌ | 5432 |
5432 |
DB_POSTGRESDB_DATABASE |
❌ | n8n |
n8n_production |
DB_POSTGRESDB_USER |
✅ | postgres |
n8n_user |
DB_POSTGRESDB_PASSWORD |
✅ | — | strong_random_password |
DB_POSTGRESDB_POOL_SIZE |
❌ | 2 |
20 (production single‑instance) |
DB_POSTGRESDB_SSL_ENABLED |
❌ | false |
true (managed cloud databases, RDS, Cloud SQL) |
How do you migrate an existing n8n instance from SQLite to PostgreSQL without data loss?
The safest migration path uses n8n’s built‑in export/import CLI
commands (available since v1.67, November 2025). Step 1:
Export entities — run
n8n export:entities --type=all --output=export/ while the
SQLite instance is stopped (exporting while running can
capture an inconsistent state if a webhook fires mid‑export).
Step 2: Set up PostgreSQL with a dedicated user and
database. Step 3: Update your .env file
with DB_TYPE=postgresdb and all
DB_POSTGRESDB_* variables. Step 4: Start
n8n with PostgreSQL and run
n8n import:entities --input=export/ --type=all.
[2]
[8]
An alternative manual method uses DBeaver — connect to
both the old SQLite and new PostgreSQL databases simultaneously, then
export tables directly. The migration order that works reliably:
credentials_entity → workflows_entity → folder → user → project
→ project_relation → tag_entity → workflows_tags → webhook_entity →
shared_credentials → shared_workflow → execution_entity. After
migrating core tables, delete the default user on the new instance, then
migrate execution history. The N8N_ENCRYPTION_KEY must be
identical between the old and new instances — if you change it, all
stored credentials become permanently unrecoverable. For a full
production‑ready deployment with Docker Compose, see the
n8n Docker Compose production stack guide.
n8n export:entities --type=all --output=export/.
(3) Set up PostgreSQL with a separate database for n8n. (4) Update
.env: DB_TYPE=postgresdb + all
DB_POSTGRESDB_* variables. (5) Start n8n with PostgreSQL. (6)
Import entities:
n8n import:entities --input=export/ --type=all. (7) Verify
workflows, credentials, and webhook URLs. Never delete the old
SQLite database file until the new PostgreSQL setup has been running
successfully for at least 48 hours.
[2]
How does n8n purge execution data and reclaim disk space on SQLite versus PostgreSQL?
n8n implements a two‑stage pruning process: soft‑deletion marks records for removal and hard‑deletion permanently deletes them after a safety buffer. Pruning triggers when either condition is met — the execution finished more than EXECUTIONS_DATA_MAX_AGE hours ago (default: 336 hours / 14 days), or the total execution count exceeds EXECUTIONS_DATA_PRUNE_MAX_COUNT (default: 10,000). Only finished (non‑active) executions are eligible. [5] [9]
The critical difference: SQLite marks deleted rows as
reusable within the database file but does not release
that space back to the operating system — the
database.sqlite file grows indefinitely. To reclaim disk
space, you must run VACUUM, which rebuilds the entire
database file — a blocking operation requiring downtime for large files.
Set DB_SQLITE_VACUUM_ON_STARTUP=true to VACUUM automatically
on restart. PostgreSQL uses autovacuum to reclaim and
release disk space continuously without downtime. For reducing saved
data before pruning, set
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none to skip saving
successful execution data, and
EXECUTIONS_DATA_SAVE_ON_PROGRESS=false to skip
intermediate node‑level progress snapshots. For database sizing guidance,
a small n8n instance (100 executions/day) generates roughly 1 GB of
execution data per month without pruning. For the complete binary data
and external storage configuration, see the
n8n Node Execution Hub.
How do you enable SQLite WAL mode and troubleshoot the silent PostgreSQL fallback?
SQLite defaults to a rollback journal mode that allows
only one writer at a time — any concurrent write attempt returns a
SQLITE_BUSY error. Enable Write‑Ahead Logging
(WAL) by setting DB_SQLITE_POOL_SIZE to an integer
greater than zero (recommended: 4). WAL mode allows
concurrent reads and a single writer to coexist — readers don’t block
writers and vice versa. This dramatically reduces SQLITE_BUSY
errors on read‑heavy workloads. n8n v2.0 shipped a new SQLite pooling
driver that can be up to 10× faster for reads.
[1]
[10]
The most common PostgreSQL configuration failure: silent fallback
to SQLite. n8n does not throw an error when it cannot connect
to PostgreSQL — it silently reverts to SQLite. Verify PostgreSQL is
active by checking the pod or container logs; the startup should mention
“Connected to database: postgresdb” or similar. If the logs show SQLite
or contain “database migration” followed by SQLite mentions, restart
from a fresh state and validate the db:postgresdb ENV var
is being read. A second common issue: PostgreSQL trigger
flooding — a Postgres Trigger set to fire on every row change
can flood n8n with executions, causing MaxClientsInSessionMode errors
and workflow slowdowns. The fix is to move n8n to PostgreSQL, reduce
trigger concurrency, or add buffering/batching to the trigger design.
For the complete self‑hosted security blueprint, see the
n8n Node Security Hardening guide.
References
- DeepWiki — Database Configuration: SQLite vs PostgreSQL feature comparison, queue mode support, WAL mode, maintenance operations (Mar 2026)
- Serverspace — How to Run n8n with PostgreSQL and Migrate from SQLite Safely: export/import entities CLI (v1.67+), encryption key preservation, Docker Compose setup (Mar 2026)
- Contabo — n8n Queue Mode Setup Guide for VPS Scalability: SQLite corruption under multi‑process writes, PostgreSQL mandatory, Bull queue library via Redis (Feb 2026)
- n8n Documentation — Database Environment Variables: DB_TYPE=postgresdb, DB_POSTGRESDB_* variables, SSL configuration, pool size, connection timeout (2026)
- DeepWiki — Execution Data Management and Pruning: two‑stage deletion, EXECUTIONS_DATA_MAX_AGE (336h), EXECUTIONS_DATA_PRUNE_MAX_COUNT (10,000), SQLite VACUUM requirement (Apr 2026)
- LumaDock — PostgreSQL vs SQLite for n8n: Real World Trade‑offs — write throughput, history bloat, queue mode requirement, backup strategies (Dec 2025)
- CSDN — Switching n8n from SQLite to PostgreSQL: Docker Benchmark & Migration — 5‑node workflow benchmark (5 req/s, 20 req/s, 100,000 history records), Docker setup, migration guide (Feb 2026)
- n8n Community — HOW TO: Migrate from SQLite to Postgres — DBeaver table‑by‑table migration order (credentials → workflows → folder → user → project), license key re‑entry (2024, updated 2025)
- Toolient — Common Performance Bottlenecks in n8n: execution data pruning settings, retention limits (336h = 14 days), EXECUTIONS_DATA_PRUNE=true (Dec 2025)
- Toolient — SQLite vs Postgres in n8n: Which Database to Use — production suitability, WAL mode, concurrency limitations, scaling thresholds (Dec 2025)

