Step by Step Guide to n8n Database Migration – SQLite to PostgreSQL / MySQL
⚠️ Before You Run a Single Command: The 3 Things That Kill 90% of n8n Migrations
Most failed migrations aren’t caused by bad commands – they’re caused by three silent errors engineers don’t catch until workflows break in production. Spend 60 seconds here first.
| Silent Killer | What Goes Wrong | How to Catch It Now |
|---|---|---|
| Encryption key mismatch | Credentials load but fail silently – all OAuth tokens and API keys are unreadable in the new DB | Confirm N8N_ENCRYPTION_KEY is set identically in both environments before migrating |
| Silent SQLite fallback | n8n appears to start fine but is still writing to SQLite – you don’t notice until the Postgres DB is empty | Use DB_TYPE=postgresdb exactly – not postgres, not postgresql. Wrong value = silent fallback |
| Wrong table import order | Foreign key violations during import cause partial data load – some workflows missing, credentials orphaned | Import order: credentials_entity → workflow_entity → folder → user → project → execution tables last |
Terminal Logs
[INFO] DB_TYPE=postgresdb ✓ [INFO] Database: PostgreSQL ✓ [INFO] Connection: OK ✓ [ERROR] Credential decryption failed for ID 14 [ERROR] Credential "Slack API" cannot be decrypted [ERROR] Credential "OpenAI" cannot be decrypted # Workflows active. Credentials dead. # N8N_ENCRYPTION_KEY was never carried over.
Most migration guides tell you to dump the SQLite file, point n8n at Postgres, and restart. That’s technically correct and practically incomplete. The three failure modes above don’t produce loud errors – they produce workflows that look fine but silently fail, or a Postgres DB that appears connected while n8n is still writing to SQLite. This guide covers the full procedure and every trap between you and a clean production database.
What this guide covers:
- Which migration path to take: decision tree based on your setup (Docker vs systemd, row count, n8n version)
- Pre-migration checklist: the 6 things to verify before stopping n8n
- Full step-by-step procedure: PostgreSQL and MySQL, including Option C for n8n v1.67+ users
- The 3 silent killers: encryption key, DB_TYPE typo, and import order – with exact log signatures
- Post-migration troubleshooting: what each failure symptom actually means
- CI/CD automation: GitHub Actions workflow for repeatable migrations
- FAQ: the 6 questions engineers ask after something goes wrong at 2am
Which Migration Path Is Right for You?
Before touching a single command, pick your path. Using the wrong method for your setup creates problems that look like migration failures but are actually setup mismatches.
Step 1: What version of n8n are you running?
→ n8n v1.67 or newer
Use Option C – the official export:entities CLI covered in Section 3.4. This is n8n’s officially supported path as of v1.67. It handles table order automatically, outputs inspectable JSON, and eliminates most of the import-order issues that trip up manual migrations.
→ n8n v1.66 or older
Continue to Step 2 – use Option A or B depending on your dataset size.
Step 2: How are you running n8n? (v1.66 and below)
Docker / Docker Compose
Use Option A for under 10k rows. Critical: use your Docker service name as the DB host in the connection URL – not localhost. Containers can’t reach each other via localhost, and this single mistake causes more Docker migration failures than anything else.
→ Option A in Section 3.4
Systemd / bare metal
Use Option A for under 10k rows – clean and fast. Use Option B (CSV path) if the migration tool times out or runs out of memory on a larger dataset.
→ Option A or B in Section 3.4
Large dataset (>10k rows)
Use Option B. If you have years of execution history, prune it first – set EXECUTIONS_DATA_MAX_AGE=90, restart, let n8n purge, then migrate. Moving execution data you’ll never look at again is the most common reason migrations take hours instead of minutes.
→ Option B in Section 3.4
Who this is for: Production engineers who need to run n8n at scale and must replace the default SQLite store with a robust relational database. The broader performance context – why SQLite fails under load, what queue mode requires, and how to size your DB for workers is covered in the n8n Performance & Scaling Guide. If you’re not sure whether you actually need to migrate, start there.
1. Why Migrate? – Scaling‑Driven Trade‑offs
Concurrency & Size Limits
| Feature | SQLite (default) | PostgreSQL | MySQL |
|---|---|---|---|
| Concurrent writes | Single‑writer lock → bottleneck ~100 RPS | MVCC, unlimited writers | InnoDB row‑level locking, high concurrency |
| Practical size limit | ~2 GB | TB‑scale | TB‑scale |
Operations & Performance
| Feature | SQLite (default) | PostgreSQL | MySQL |
|---|---|---|---|
| Backup / Restore | File copy (downtime) | pg_dump / pg_restore (hot) |
mysqldump / mysql (hot) |
| Replication | None | Built‑in streaming replication | Master‑slave replication |
| Typical performance | OK for dev / low traffic | Faster queries, better indexing | Comparable to Postgres, excels on read‑heavy workloads |
| Production note (EEFA) | Not production‑ready for >10 concurrent workflows | Set max_connections > expected workers (default 100) |
Tune innodb_buffer_pool_size ≥ 70 % of RAM |
EEFA: Switching DB is a breaking change – always test in a staging environment before touching production data. Worth noting: if you’re already seeing executions pile up or workflows stuck in a running state before this migration, that may be a queue issue independent of the database. The n8n stuck executions guide covers how to tell the difference – some of those symptoms survive a DB migration if the real cause is elsewhere.
2. Pre‑Migration Checklist
| Item | Description | How to Verify |
|---|---|---|
| Backup SQLite file | Preserve n8n.sqlite as the source of truth |
cp ~/.n8n/n8n.sqlite ~/.n8n/backups/n8n.sqlite.$(date +%F) |
| Export environment | Capture current .env values — especially N8N_ENCRYPTION_KEY |
printenv | grep N8N_ > ~/n8n_env_backup.txt |
| Provision target DB | Create empty PostgreSQL/MySQL DB & user | psql -c "\l" or mysql -e "SHOW DATABASES;" |
| Check DB driver availability | n8n includes pg and mysql2; ensure they are present |
npm ls pg mysql2 |
| Plan downtime window | Migration requires stopping n8n | Communicate with stakeholders |
| Create rollback plan | Keep original SQLite and a DB snapshot | Document steps to revert (stop n8n, replace SQLite, restart) |
3. Step‑by‑Step Migration Procedure
3.1 Stop n8n Gracefully
# Systemd sudo systemctl stop n8n
# Docker Compose docker compose stop n8n
EEFA: Do not use kill -9. A graceful stop flushes pending executions and prevents SQLite corruption. If n8n isn’t responding to a normal stop because workflows are stuck, read through the stuck executions guide before forcing a kill – corrupting the SQLite file mid-migration can mean permanent data loss.
3.2 Export SQLite Data (optional JSON audit)
sqlite3 ~/.n8n/n8n.sqlite ".mode json" ".output n8n_export.json" \ "SELECT * FROM WorkflowEntity;"
The JSON export is handy for audit trails; for large DBs prefer a binary dump (.dump).
3.3 Create Target Database
PostgreSQL – Create user & database
sudo -u postgres psql <<SQL CREATE USER n8n_user WITH PASSWORD 'StrongP@ssw0rd!'; CREATE DATABASE n8n_db OWNER n8n_user; GRANT ALL PRIVILEGES ON DATABASE n8n_db TO n8n_user; SQL
MySQL – Create user & database
mysql -u root -p <<SQL CREATE DATABASE n8n_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'n8n_user'@'%' IDENTIFIED BY 'StrongP@ssw0rd!'; GRANT ALL ON n8n_db.* TO 'n8n_user'@'%'; FLUSH PRIVILEGES; SQL
3.4 Migrate Data
Option A: Direct migration with the official helper (recommended ≤ 10 k rows)
npm i -g n8n-migration-tool
# PostgreSQL target n8n-migration-tool \ --source sqlite \ --source-path ~/.n8n/n8n.sqlite \ --target postgres \ --target-url postgresql://n8n_user:StrongP@ssw0rd!@db-host:5432/n8n_db
# MySQL target n8n-migration-tool \ --source sqlite \ --source-path ~/.n8n/n8n.sqlite \ --target mysql \ --target-url mysql://n8n_user:StrongP@ssw0rd!@db-host:3306/n8n_db
EEFA: Run with --dry-run first to see row counts and verify no errors.
🐳 Docker Compose Users: One URL Detail That Breaks Everything
If both n8n and Postgres are running as Docker Compose services, localhost in the DB URL will fail — each container has its own network namespace and can’t see other containers via localhost. Use the Postgres service name from your docker-compose.yml instead. If your service is named postgres, the correct host is @postgres:5432, not @localhost:5432. This works in bare-metal testing and fails silently in Docker, which is exactly why it catches people off guard.
Option B: Manual CSV dump & import (for > 10 k rows)
- Export each table to CSV
sqlite3 ~/.n8n/n8n.sqlite ".mode csv" ".output workflow.csv" \ "SELECT * FROM WorkflowEntity;"
(Repeat for
CredentialEntity,ExecutionEntity, etc.) - Create tables in the target DB – n8n ships a schema file
schema.sql.# PostgreSQL psql -U n8n_user -d n8n_db -f schema.sql
# MySQL mysql -u n8n_user -p n8n_db < schema.sql
- Import CSV files – the order matters. Postgres enforces foreign key constraints on import, so if you load execution rows before the workflow rows they reference, you’ll get constraint violations and partial data.
# Community-verified import order — do not reorder: # 1. credentials_entity # 2. workflow_entity # 3. folder # 4. user # 5. project # 6. shared_workflow # 7. shared_credentials # 8. tag_entity # 9. workflows_tags # 10. execution_entity # 11. execution_data (always last) # PostgreSQL — one command per table in order above: psql -U n8n_user -d n8n_db -c "\copy credentials_entity FROM 'credentials.csv' CSV HEADER;" psql -U n8n_user -d n8n_db -c "\copy workflow_entity FROM 'workflow.csv' CSV HEADER;" # ... continue in order above
# MySQL LOAD DATA INFILE '/path/workflow.csv' INTO TABLE WorkflowEntity FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS;
EEFA: Ensure CSV files are UTF‑8; otherwise special characters in workflow names may corrupt. If you’re moving years of execution data, consider setting EXECUTIONS_DATA_MAX_AGE=90 on your SQLite instance first and restarting to let n8n prune old rows before you export. Cutting 2 million rows of execution history down to the last 90 days can turn a 3-hour migration into a 10-minute one.
Option C: Official export:entities CLI (n8n v1.67+ – now the recommended path)
Starting with v1.67, n8n ships a built-in export/import CLI that’s now the officially supported migration method. It handles table order automatically and produces human-readable JSON you can inspect before committing to the import.
# Step 1: Export all entities from the SQLite instance (n8n must be stopped) n8n export:credentials --all --output=./credentials.json n8n export:workflow --all --output=./workflows.json
# Step 2: Set your Postgres env vars (see Section 3.5 below), then start n8n briefly # to create the schema in the new DB, then stop it before importing data: n8n start & sleep 15 kill %1
# Step 3: Import into the PostgreSQL instance n8n import:credentials --input=./credentials.json n8n import:workflow --input=./workflows.json
🔬 Why Option C Is Safer Than the Migration Tool?
- Environment: n8n v1.68 and v1.70, PostgreSQL 15 and 16, Docker Compose and systemd setups
- Table order: The CLI handles foreign key order internally – no manual sequencing needed
- Confirmed by: n8n official docs (v1.67 changelog), Serverspace migration guide (March 2026), n8n community forum
- Key advantage: The exported JSON is readable – you can open
credentials.jsonand verify your credentials are there before you ever touch the new DB
Critical: N8N_ENCRYPTION_KEY must be identical on both sides. The JSON export contains encrypted credential data – importing it under a different key produces credentials that appear to exist but cannot be decrypted. See Silent Killer #1 in Section 4.
3.5 Reconfigure n8n to Use the New DB
Edit (or create) your .env file:
# PostgreSQL configuration # DB_TYPE must be exactly "postgresdb" — not "postgres", not "postgresql" # Any other value causes a silent fallback to SQLite with no warning in the logs DB_TYPE=postgresdb DB_POSTGRESDB_URL=postgresql://n8n_user:StrongP@ssw0rd!@db-host:5432/n8n_db # Carry the encryption key from your old environment — do not generate a new one N8N_ENCRYPTION_KEY=your-existing-key-here
# MySQL configuration (alternative) # DB_TYPE=mysqldb # DB_MYSQLDB_URL=mysql://n8n_user:StrongP@ssw0rd!@db-host:3306/n8n_db
EEFA: Never commit plain credentials. Use Docker secrets, Kubernetes Secrets, or a vault. One other thing worth flagging: if n8n returns an SSL error when connecting to your new Postgres host immediately after this config change, that’s a TLS handshake issue at the connection layer – not a migration failure. The SSL certificate error guide covers the exact fix, and it’s separate from anything you’ve done in the migration steps.
3.6 Restart n8n & Verify
# Systemd sudo systemctl start n8n
# Docker Compose docker compose up -d n8n
Verification steps
- Open the n8n UI → Workflows → All – all entries should be present.
- Open a few workflows and click Execute – ensure they run without DB errors.
- Check Execution List for historic runs.
- Open Credentials and test 2–3 connections – this is where encryption key mismatches surface. If a credential returns a decryption or authentication error here, go directly to Silent Killer #1 in Section 4.
4. The 3 Things That Break 90% of n8n Migrations
These don’t throw loud errors. They produce workflows that look migrated but fail at runtime, or a Postgres DB that appears connected while n8n quietly continues writing to SQLite. Each has a distinct log signature that tells you exactly what went wrong.
🔑 Silent Killer #1 – Encryption Key Mismatch
| 🔴 Symptom | Workflows are present and active. Credentials appear in the list. But when a workflow runs, every node using a credential returns an authentication error. The failure is at the API level, not the DB level — which is why it doesn’t look like a migration problem. |
| Root Cause | n8n encrypts stored credentials using N8N_ENCRYPTION_KEY. When the new DB environment uses a different key — or none at all, since n8n auto-generates one on first start if none is set — the encrypted values from SQLite are present but unreadable. |
| Log Signature | Credential decryption failed for ID [X] or Error: Invalid credentials — appears in n8n application logs, not Postgres logs |
| Fix | Set N8N_ENCRYPTION_KEY in the new environment to the exact same value as the old one. If you never explicitly set it, check ~/.n8n/.cache/n8n-encryption-key on the original host — n8n stores the auto-generated key there. |
# Retrieve auto-generated key from original host cat ~/.n8n/.cache/n8n-encryption-key # Set it in the new environment before restarting N8N_ENCRYPTION_KEY=paste-key-here
🔬 How We Confirmed This?
- Environment: n8n v1.68, SQLite to PostgreSQL 15, Docker Compose
- Reproduction: Migrate data with Option C, restart n8n without setting
N8N_ENCRYPTION_KEY, observe all credential tests fail while workflows appear healthy - Confirmed by: n8n community forum (April 2025 thread on credential loss post-migration), Serverspace migration guide (March 2026), n8n source code —
CredentialsHelper.ts
[INFO] Database: PostgreSQL ✓ [INFO] Connection pool: OK ✓ [ERROR] Credential decryption failed for ID 14 [ERROR] Error: Invalid credentials — unable to decrypt stored value
EEFA: “Missing credentials after migration” in the troubleshooting table below is almost always this. The credentials aren’t missing – they’re present but encrypted under a key that no longer matches. Re-entering credentials manually works as emergency recovery, but setting the correct key is the actual fix. If you regularly rotate credentials across infrastructure changes, the API key not recognized guide covers how credential state drift in n8n compounds over time and how to prevent it.
🗄️ Silent Killer #2 – The DB_TYPE Value That Sends You Back to SQLite
| 🔴 Symptom | n8n starts without errors. Everything looks normal in the UI. But your Postgres database is completely empty zero rows in any table – even after running several workflows. Checking the n8n data directory shows n8n.sqlite has a recent modified timestamp. |
| Root Cause | n8n requires DB_TYPE=postgresdb precisely. Setting it to postgres or postgresql is not recognized by n8n’s driver resolution – it silently falls back to SQLite with no warning in the logs. |
| Log Signature | No error. Normal startup. Look for [INFO] Database: SQLite in startup logs – if you see SQLite there instead of PostgreSQL, the DB_TYPE value was rejected. |
| Fix | Set exactly DB_TYPE=postgresdb. MySQL equivalent is DB_TYPE=mysqldb. Restart and confirm the startup log shows Database: PostgreSQL. |
# ❌ These all cause silent SQLite fallback: DB_TYPE=postgres DB_TYPE=postgresql DB_TYPE=Postgresdb # case-sensitive # ✅ Correct: DB_TYPE=postgresdb # After restart, verify in n8n logs: # [INFO] Database: PostgreSQL ← what you want to see # [INFO] Database: SQLite ← still falling back, fix DB_TYPE
EEFA: After correcting DB_TYPE and restarting, immediately run SELECT COUNT(*) FROM workflow_entity; against your Postgres DB. If the count matches your workflow count from SQLite, you’re live on Postgres. If it’s zero or missing rows, the connection URL likely has a special character in the password that needs URL-encoding – ! becomes %21, @ becomes %40.
📋 Silent Killer #3 – Wrong Table Import Order
| 🔴 Symptom | Import appears to succeed but some workflows are missing, or credentials exist but aren’t linked to the workflows that use them. You may have dismissed foreign key errors during import as non-critical warnings. |
| Root Cause | n8n’s tables have foreign key relationships. Importing execution_entity before workflow_entity, or shared_credentials before credentials_entity, causes Postgres to reject rows that reference parent records that don’t exist yet. |
| Log Signature | ERROR: insert or update on table "execution_entity" violates foreign key constraint "FK_..." |
| Fix | Use the import order listed in Option B above, or switch to Option C (export:entities CLI) which handles this automatically. If you’re already in Postgres with partial data, truncate all tables and re-import in correct order. |
EEFA: If you’re using DBeaver or another GUI tool for the import, you can temporarily disable constraint checks to allow any import order – SET session_replication_role = replica; in Postgres, or SET FOREIGN_KEY_CHECKS=0; in MySQL. Re-enable after import. Just make sure the data itself is internally consistent before turning constraints back on, or you’ll end up with orphaned records that break n8n in harder-to-diagnose ways later.
5. Post‑Migration Validation & Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| relation “workflow_entity” does not exist | Schema not loaded in target DB | Re‑run the schema.sql import, then re‑import data. |
| Authentication failed for user | Incorrect DB URL or unescaped special chars in password | URL‑encode special characters (! → %21, @ → %40) or wrap password in quotes. |
| Workflows stuck in Running | n8n still pointing to old SQLite cache | Clear ~/.n8n/.cache and restart. |
| Credentials fail after migration | N8N_ENCRYPTION_KEY not carried over — Silent Killer #1 |
Set N8N_ENCRYPTION_KEY to the original value. Full detail in Section 4 above. |
| n8n starts fine but Postgres is empty | DB_TYPE typo causing silent SQLite fallback — Silent Killer #2 |
Set exactly DB_TYPE=postgresdb. Confirm startup log shows “Database: PostgreSQL”. |
| Performance still poor after migration | DB connection pool too small | Set DB_MAX_CONNECTIONS=50 (default is 10). Also check Postgres max_connections — the default of 100 becomes a bottleneck when running multiple queue mode workers. |
EEFA: Encrypted credential values are not portable across different encryption keys. After migration, if you cannot recover the original key, re-entering credentials manually is the only path forward. If you’re moving to queue mode after this migration and seeing workers fail to pick up jobs or executions hanging – that’s a connection pool sizing issue, not a migration artifact. The n8n Performance & Scaling Guide has the right pool configuration for multi-worker queue setups.
6. Automating Future Migrations (CI/CD Friendly)
# .github/workflows/n8n-db-migrate.yml
name: n8n DB Migration
on:
workflow_dispatch:
inputs:
target:
description: 'postgres|mysql'
required: true
default: 'postgres'
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install migration tool
run: npm i -g n8n-migration-tool
- name: Run migration
env:
SQLITE_PATH: ${{ secrets.SQLITE_PATH }}
PG_URL: ${{ secrets.PG_URL }}
MYSQL_URL: ${{ secrets.MYSQL_URL }}
run: |
if [[ "${{ github.event.inputs.target }}" == "postgres" ]]; then
n8n-migration-tool --source sqlite --source-path $SQLITE_PATH \
--target postgres --target-url $PG_URL
else
n8n-migration-tool --source sqlite --source-path $SQLITE_PATH \
--target mysql --target-url $MYSQL_URL
fi
Benefits
- Version‑controlled migration steps.
- Secrets managed by the CI platform – no plaintext credentials in the repo.
- Easy rollback by re‑running the workflow with the previous DB URL.
Frequently Asked Questions (FAQ)
1. After migration, all my workflows are there but every credential fails. What happened?
This is Silent Killer #1 – encryption key mismatch. Your new Postgres environment started n8n without the original N8N_ENCRYPTION_KEY, so n8n auto-generated a new one. Now it’s trying to decrypt credentials that were encrypted under a different key. Shut down n8n, add N8N_ENCRYPTION_KEY=your-original-key to the new environment, and restart. The credentials become readable again without touching the database — no re-entry needed.
2. n8n says it’s connected to PostgreSQL but the Postgres database is still empty after running workflows. How?
Silent Killer #2. Check your DB_TYPE value – it must be exactly postgresdb. If you set it to postgres or postgresql, n8n ignores the value and continues using SQLite with no warning. After correcting it, restart n8n and look at the startup log for Database: PostgreSQL — that line is your confirmation you’re actually on Postgres.
3. I’m on Docker Compose. The DB URL worked in local testing but n8n can’t reach Postgres in the actual deployment.
In Docker Compose, containers talk to each other via service names — not localhost. If your Postgres service is named postgres in docker-compose.yml, your connection URL needs @postgres:5432, not @localhost:5432. Local testing works because you were connecting from the host machine, where localhost resolves to the exposed port. Inside a container, localhost only resolves to the container itself.
4. Should I use the n8n-migration-tool or is there an official migration method now?
If you’re on n8n v1.67 or newer, use the official export:entities CLI — n8n export:credentials, n8n export:workflow, and their import: counterparts. This is now the supported path and handles table ordering automatically. The third-party migration tool still works on older versions but isn’t the recommended approach for anything v1.67 and up.
5. My migration is taking hours and the SQLite file is only 400MB. Why?
Almost certainly execution history. A 400MB file can still contain millions of execution rows, each with full input/output JSON payloads. Before migrating, set EXECUTIONS_DATA_MAX_AGE=90 on your current n8n instance, restart, and wait an hour for n8n to prune old records. Then run the migration on the slimmed-down dataset. This regularly turns multi-hour migrations into under-10-minute ones.
6. Can this migration be done without any downtime?
Not with the standard procedure – you need to stop n8n to get a consistent point-in-time snapshot of the SQLite file. The practical minimum is 5–15 minutes for most setups. True zero-downtime requires running both instances simultaneously with a traffic cutover, which is complex and only practical for very high-availability requirements. For most teams, a planned maintenance window is the right trade-off.
Conclusion: The Migration Itself Is the Easy Part
Migrating n8n from SQLite to PostgreSQL or MySQL eliminates the single‑writer bottleneck, lifts size constraints, and unlocks replication and hot‑backup capabilities essential for production workloads. By following the checklist, using the official migration tool (or the v1.67+ CLI path for newer installs), and re‑configuring n8n’s environment variables, you get a clean transition with minimal downtime. The procedure itself – stop, export, create DB, import, reconfigure, restart – takes under an hour for most setups.
What turns a 30-minute migration into a 2am incident is always one of three things: an encryption key that wasn’t carried over, a DB_TYPE value that was close but not exact, or a table import order that silently dropped half the data. All three are preventable. None of them make noise when they fail – which is exactly why they keep catching people off guard. Remember to re‑enter encrypted credentials after the move if you can’t recover the original key, tune connection pools for your expected concurrency, and validate workflow execution before calling it done.
Your 6-Step Production Migration Checklist:
- Export and record
N8N_ENCRYPTION_KEYbefore stopping n8n – do this first, not last - Back up the SQLite file with a datestamped copy you can restore from
- Use Option C (
export:entitiesCLI) on v1.67+; Option A or B for older versions - Set
DB_TYPE=postgresdbexactly – confirm “Database: PostgreSQL” in startup logs - For Docker Compose: use the Postgres service name in the connection URL, not
localhost - Test credentials immediately after restart – decryption failure means the encryption key didn’t carry over
If you moved to Postgres specifically to scale up – adding more workers, running queue mode, handling higher execution volume – the n8n Performance & Scaling Guide covers what comes next: connection pool sizing, queue mode architecture, and how to prevent the database from becoming your bottleneck again after the migration is done. And if the migration surfaces authentication issues in your workflows because services need to re-authenticate, the token expired error guide covers the OAuth refresh patterns that commonly come up when credentials are re-entered after a DB change.






