n8n with Postgres instead of SQLite: when and how to switch
- Signs you should switch
- A Compose setup with Postgres
- The encryption key is critical
- Migrating from SQLite
- Prune execution data
- Webhook URL
- Backups
- Scaling later
- Common errors after switching
n8n ships with SQLite by default, which is perfect for trying it out. Once workflows matter to your business, PostgreSQL is the better backend: it handles concurrent executions, larger execution history and proper backups much more gracefully.
Signs you should switch
- Workflows run frequently and execution history grows large
- You see “database is locked” errors under load
- You want queue mode with separate worker containers
- You need reliable, consistent backups while n8n is running
A Compose setup with Postgres
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: change-me
POSTGRES_DB: n8n
volumes: [pg_data:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
n8n:
image: n8nio/n8n:latest
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: change-me
N8N_ENCRYPTION_KEY: a-long-random-string
WEBHOOK_URL: https://n8n.example.com/
depends_on:
postgres: { condition: service_healthy }
volumes: [n8n_data:/home/node/.n8n]
volumes:
pg_data:
n8n_data:
The encryption key is critical
n8n encrypts stored credentials with N8N_ENCRYPTION_KEY. If you lose it or it changes, every saved credential becomes unreadable. Set it explicitly, store it in a password manager, and use the same value when migrating.
Migrating from SQLite
- Export workflows and credentials using the n8n CLI (
n8n export:workflow --allandn8n export:credentials --all) from the old instance. - Note the existing encryption key from the old
.n8n/configfile. - Start the new instance with Postgres and the same encryption key.
- Import with
n8n import:workflowandn8n import:credentials. - Test a few workflows, then switch the webhook domain.
Prune execution data
Execution history grows fast. Enable pruning:
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
This keeps a week of history and stops the database from ballooning.
Webhook URL
Set WEBHOOK_URL to the public HTTPS address. Otherwise n8n generates webhook URLs with internal hostnames and integrations fail to call back.
Backups
Back up Postgres with pg_dump nightly and keep the encryption key separately. Without the key, a database backup restores workflows but not credentials.
Scaling later
With Postgres in place, you can enable queue mode with Redis and run separate worker containers when a single process is no longer enough.
Common errors after switching
- Connection refused on start: n8n started before Postgres was ready. The health check and
depends_oncondition above fix this. - Credentials show as invalid: the encryption key differs from the old instance. Restore the original key rather than re-entering every credential.
- Webhooks return 404: the workflow is not active, or the proxy strips the path. Check that test and production webhook URLs both point at the public domain.
- Slow editor with big executions: large binary data is stored in the database. Set
N8N_DEFAULT_BINARY_DATA_MODE=filesystemso files go to the volume instead.
A step by step deployment including domain and SSL is in this guide to deploying n8n with PostgreSQL.
Disclosure: I work on Peon, which has an n8n template, but the configuration above is plain Docker Compose.
Write a comment