Automation

When Should You Migrate n8n from SQLite to PostgreSQL?

Switch to PostgreSQL when your n8n execution history exceeds 5,000 rows or you encounter SQLITE_BUSY errors during concurrent workflow executions.

6 min read
When Should You Migrate n8n from SQLite to PostgreSQL?

At Hostna, we manage the underlying server infrastructure to ensure that automation tools like n8n run without the overhead of manual database maintenance. However, as your workflow volume increases, the default database engine, SQLite, often hits architectural limits that can cause execution delays or data corruption. The decision to migrate to PostgreSQL is not merely about scale; it is about how the database handles concurrent write operations and disk I/O under load.

Symptom Technical Cause Recommended Fix
SQLITE_BUSY: database is locked Concurrent write attempts during simultaneous workflow triggers. Migrate to PostgreSQL for row-level locking.
High iowait in top Synchronous disk writes for every execution log entry. Enable WAL mode or move to a networked database.
UI hangs on ‘Executions’ tab Sequential scanning of a large .sqlite file (over 2 GB). Index optimization or migration to PostgreSQL.

The Architecture of SQLite versus PostgreSQL in n8n

SQLite is a file-based database engine that excels in simplicity. In a standard n8n deployment, every execution, trigger log, and workflow save results in a write operation to a single file on the disk. The primary limitation arises from SQLite’s locking mechanism. When one process writes to the file, the entire database is locked. If a second workflow triggers at the exact same millisecond, it must wait. If it waits too long, you will see the SQLITE_BUSY error in your logs.

PostgreSQL, by contrast, uses a client-server model. It supports multiple concurrent connections and uses Multi-Version Concurrency Control (MVCC) to allow readers and writers to operate simultaneously without blocking each other. This is critical for dedicated automation environments where high-frequency webhooks or parallel loops are common. While SQLite can be tuned using Write-ahead logging (WAL), this only mitigates the issue rather than solving it for multi-user or high-concurrency environments.

Identifying the Quantitative Threshold for Migration

There is no single row count that dictates a migration, but technical patterns emerge based on the complexity of your workflows. If your database.sqlite file exceeds 2 GB, the performance of internal n8n queries for the execution list begins to degrade. You can check your current database size by running the following command in your terminal:

find . -name "database.sqlite" -exec du -sh {} +

A second metric is the execution history. If you maintain more than 5,000 execution records without aggressive pruning, the overhead of indexing these rows in a single file becomes a bottleneck. In these scenarios, standard Linux distributions running n8n will show elevated CPU usage in the iowait state, indicating the processor is idling while waiting for the disk to complete write operations. If you observe %util values consistently above 80 in iostat -x 1, the storage subsystem is saturated by SQLite’s synchronous writes.

Daily Executions Database Recommended Hardware Requirement
0 – 500 SQLite (Default) 2 GB RAM / 1 vCPU
500 – 5,000 SQLite (WAL Mode) 4 GB RAM / 2 vCPU
5,000+ PostgreSQL 8 GB RAM / 4 vCPU

Technical Mechanics of the Migration

Migrating from SQLite to PostgreSQL requires a structured approach because the data types and schema handling differ between the two engines. The most reliable method involves exporting your existing workflows and credentials using the n8n CLI, rather than attempting a raw SQL conversion. This ensures that the data remains consistent with the n8n application logic. You can perform an export with this command:

docker exec -it n8n_container n8n export:workflow --all --output=/backup/workflows.json

Once the data is exported, you must reconfigure your environment variables to point to a PostgreSQL instance. Key variables include DB_TYPE=postgresdb, DB_POSTGRESDB_HOST, and DB_POSTGRESDB_PORT. When n8n starts with these variables, it will automatically create the necessary schema in the target database. Following the schema creation, you can import your data. Detailed steps are available in the official n8n database migration guide which covers the environment variable syntax for different deployment types.

Memory and Resource Management Post-Migration

PostgreSQL introduces a separate process that requires its own memory allocation. On a virtual server, you must balance the memory available to the n8n Node.js process and the PostgreSQL service. In a Docker environment, the cgroup memory limit acts as a hard ceiling; if n8n exceeds this during a large JSON transformation, the kernel OOM killer will terminate the process to protect the host. This is particularly relevant when isolated compute resources are in use, as there is no burstable swap to absorb sudden spikes.

For the PostgreSQL side, the shared_buffers parameter should typically be set to roughly one quarter of the machine’s total RAM to ensure efficient caching. On the n8n side, use the DB_POSTGRESDB_POOL_MAX environment variable to control the number of concurrent connections. A default value of 10 is usually sufficient, but if you have hundreds of simultaneous workers, you may need to increase this while ensuring the PostgreSQL max_connections setting is high enough to accommodate the pool.

Data Integrity and Backup Procedures

One of the strongest arguments for PostgreSQL is the robustness of its backup mechanisms. Backing up a live SQLite database is risky; if a write occurs while you are copying the database.sqlite file, the backup can be corrupted. While you can use sqlite3 database.sqlite "VACUUM INTO 'backup.sqlite';", it is a heavy operation. PostgreSQL allows for non-blocking backups using pg_dump, which captures a snapshot of the database without stopping the application.

Our commitment to strict data processing standards means that we recommend regular automated backups regardless of the database engine. However, PostgreSQL provides the ability to implement Point-In-Time Recovery (PITR) by archiving Write-Ahead Logs. This allows you to restore the database to a specific second, a feature that is structurally impossible with standard SQLite deployments. This level of protection is a core part of our service level agreement when managing complex automation stacks.

Handling Common Failure Modes

If you choose to stay on SQLite but are experiencing performance issues, the first step is to check database integrity. Run sqlite3 database.sqlite "PRAGMA integrity_check;" to ensure there is no underlying corruption. If the file is healthy but slow, you can attempt to reclaim space and rebuild indexes with the VACUUM; command. Note that this requires enough free disk space to create a temporary copy of the entire database file.

For those on entry-level hosting plans, resource contention is the most common cause of database timeouts. If the CPU steal time is high, your write operations will take longer, increasing the window where a SQLITE_BUSY lock can occur. Monitoring these metrics is part of the shared security responsibility between the host and the operator. All resources allocated through Hostna can be monitored to identify these bottlenecks before they result in workflow failures. If your automation grows beyond the limits of a single file, migrating to a client-server database is the most sustainable path forward, provided you adhere to your acceptable use policy regarding resource consumption.

What to Provide Support for Troubleshooting

If you encounter persistent database errors after migrating, check your n8n logs for connection timeout messages. Provide our support team with the output of docker logs n8n_container_name --tail 100 and the current resource usage from top or htop. This allows us to determine if the issue lies with the database configuration, the Node.js memory limits, or the underlying storage performance. Accurate logging is the first step toward a stable automation environment.

This article was drafted with AI assistance from Hostna’s own research pipeline and published without individual review. Tell us if anything here is wrong.

Questions people also ask4
Can I use SQLite for a production n8n instance?

Yes, SQLite is suitable for production if your execution volume is low and you do not require high concurrency. However, you should enable WAL mode and implement regular pruning of execution history to prevent the database file from growing too large, which eventually causes latency in the user interface and workflow triggers.

How do I enable WAL mode in n8n for SQLite?

You can enable Write-Ahead Logging by connecting to your SQLite file using the command line and running PRAGMA journal_mode=WAL;. In n8n, this can also be set via environment variables in some configurations, though the manual SQL command is the most direct way to ensure the setting is applied to the database file.

Will migrating to PostgreSQL make my workflows faster?

Migration primarily improves concurrency and reliability rather than the raw execution speed of a single node. You will notice improved responsiveness in the UI and fewer locking errors during simultaneous triggers. For raw execution speed, focus on CPU clock speed and reducing the complexity of individual workflow functions.

What is the main risk of staying on SQLite?

The primary risk is database locking during high-concurrency events. If multiple webhooks hit your n8n instance at once, SQLite may fail to write the execution data, leading to lost logs or failed workflows. Additionally, large SQLite files are more prone to corruption during sudden server reboots or disk full events.