Skip to main content
Back to all posts Self-Hosting
• 5 min read • Hafiz Mateen Afzal • ...

5 Production Issues I Hit Self-Hosting n8n (and How I Fixed Them)

Moving n8n from local testing to a real production stack surfaces a specific set of infrastructure problems. Here's what broke, why, and the exact fixes.

Setting up and scaling an enterprise automation stack on self-hosted n8n is a genuinely useful exercise — the flexibility is unmatched. But moving from local testing to production surfaces a specific set of infrastructure problems that don't show up until you're under real load. Here's what I ran into, why it happened, and how I fixed each one.

1. SQLite Concurrency & Database Lock Errors

The problem: Under high-frequency webhooks or concurrent workflow executions, I started seeing database is locked errors, an unresponsive UI, and dropped execution logs.

Root cause: n8n defaults to SQLite for quick starts, which handles concurrent read/write operations poorly once throughput increases.

The fix: Migrated the persistent database layer to external PostgreSQL — DB_TYPE=postgresdb — and tuned the connection pool (DB_POSTGRESDB_POOL_SIZE=10-20) to handle concurrent workloads without locking.

2. Node.js Out-of-Memory Crashes on Large Payloads

The problem: Fetching thousands of records via API nodes, or parsing large CSV/JSON payloads, crashed the main n8n container randomly with Allocation failed — JavaScript heap out of memory.

Root cause: Node.js defaults to roughly a 1.5–2GB heap limit, and n8n keeps execution history in memory unless you configure pruning.

The fix:

  • Raised the V8 heap limit: NODE_OPTIONS="--max-old-space-size=4096"
  • Enabled automatic execution pruning: EXECUTIONS_DATA_PRUNE=true, EXECUTIONS_DATA_MAX_AGE=168 (7 days)
  • Used the Loop Over Items / Split in Batches node for large datasets instead of processing one giant array at once

3. Webhook Failures & Missing HTTPS Endpoints

The problem: External services (Stripe, GitHub, custom webhooks) failed to trigger workflows, throwing SSL handshake or CORS errors.

Root cause: Webhooks need a stable public HTTPS endpoint. A mismatched WEBHOOK_URL was causing n8n to hand back internal localhost URLs to external services.

The fix: Put n8n behind a reverse proxy (Nginx/Traefik/Caddy) with automated Let's Encrypt SSL, and set these explicitly:

N8N_HOST=n8n.yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com/

4. Credentials Break After Container Redeployment

The problem: After pulling a new Docker image or redeploying the stack, every stored credential (API keys, OAuth tokens) became unreadable.

Root cause: n8n generates an ephemeral encryption key automatically if you don't set one. Redeploying the container regenerates that key, which makes every encrypted credential in the database unreadable.

The fix: Set a static N8N_ENCRYPTION_KEY in the environment config and back it up in a secrets manager before the first launch.

5. Main Thread Bottlenecks Under Load

The problem: Long-running Code nodes or heavy HTTP fetches froze the UI and caused incoming webhooks to time out.

Root cause: By default, n8n runs the UI, webhook handling, and workflow execution all in a single-threaded process.

The fix: Switched to Queue Mode — added Redis as the message broker, split the main instance (UI + webhook triggers) from dedicated stateless worker instances, and set EXECUTIONS_MODE=queue so execution load gets offloaded to workers.

Dev vs. Production, at a Glance

Component Dev / Small Instance Production Setup
Database SQLite Dedicated PostgreSQL
Execution engine Single-threaded main process Multi-worker Queue Mode via Redis
Reverse proxy Direct port exposure Nginx/Traefik with automated TLS
Data retention Indefinite (high disk usage) Automated pruning enabled
Secrets Auto-generated ephemeral key Explicit N8N_ENCRYPTION_KEY
Tags: #n8n #Automation #DigitalOcean #Self-Hosting