n8n Environment Variables: Complete Configuration Reference
One missing environment variable can destroy your entire n8n deployment. Credentials become unreadable. Webhooks stop receiving data. Workflows silently fail. And you won’t know something is wrong until a client asks why their automation hasn’t run in three days.
The n8n community forums are filled with these stories. Someone migrates their instance, forgets to set the encryption key, and suddenly every credential shows “could not be decrypted.” Another user deploys behind a reverse proxy, skips the webhook URL configuration, and spends hours debugging why external services can’t reach their workflows.
Configuration mistakes are the silent killer of self-hosted n8n deployments.
The Silent Killer of n8n Deployments
Most n8n guides focus on getting the application running. They show you the basic Docker command, maybe a simple docker-compose file, and call it a day. You see the n8n interface, create a workflow, and assume everything works.
But production readiness requires much more. Environment variables control everything from how n8n encrypts your credentials to how many workflows can run simultaneously. Miss a critical setting, and your “working” deployment becomes a liability.
The worst part? Many configuration problems don’t surface immediately. Your encryption key might auto-generate on first launch, work perfectly for months, then disappear after a container restart. Your webhooks might function in testing but fail in production because the URL doesn’t match your domain.
What Most Guides Get Wrong
Documentation tends to list environment variables alphabetically without context. You see N8N_BASIC_AUTH_ACTIVE next to N8N_ENCRYPTION_KEY with no indication that one is deprecated and the other is absolutely critical.
This guide organizes variables by what they actually do, explains when you need them, and shows you real configurations that work in production. We’ll cover the mistakes we’ve seen across dozens of n8n deployments and how to avoid them.
What You’ll Learn
- How n8n’s configuration system works, including the
_FILEsuffix for secrets - Essential variables every deployment needs from day one
- Security settings that protect your credentials and data
- Database configuration for production-grade persistence
- Performance tuning to handle high workflow volumes
- Queue mode setup for scaling across multiple workers
- Logging and monitoring for operational visibility
- Complete production configurations you can copy and adapt
- Troubleshooting the most common configuration problems
How n8n Configuration Works
Before diving into specific variables, understand how n8n reads configuration. This knowledge helps you debug issues and organize your settings properly.
Configuration Methods
n8n accepts configuration through three methods, listed in order of precedence:
- Environment variables (highest priority)
- Configuration file (
configfile in n8n directory) - Default values (lowest priority)
Environment variables override everything else. This makes them ideal for Docker deployments where you define settings in docker-compose files or container orchestration platforms.
Setting variables in Bash:
export N8N_HOST=n8n.example.com
export N8N_PORT=5678
Setting variables in Docker Compose:
services:
n8n:
environment:
- N8N_HOST=n8n.example.com
- N8N_PORT=5678
Setting variables in Docker run:
docker run -it --rm \
-e N8N_HOST=n8n.example.com \
-e N8N_PORT=5678 \
docker.n8n.io/n8nio/n8n
The _FILE Suffix for Secrets
For sensitive values like database passwords and encryption keys, n8n supports loading values from files instead of environment variables directly. Append _FILE to the variable name and provide a file path.
# Instead of this (exposes password in environment)
DB_POSTGRESDB_PASSWORD=my_secret_password
# Use this (reads password from file)
DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/db_password
This pattern integrates with Docker secrets and Kubernetes secrets, keeping sensitive data out of your environment variable listings and container inspection output.
Proxy Variable Handling
n8n uses the proxy-from-env package for proxy configuration. This creates a specific precedence rule: lowercase proxy variables take priority over uppercase versions.
If you set both http_proxy and HTTP_PROXY, the lowercase version wins. Keep this in mind when debugging connectivity issues in corporate environments.
Essential Variables for Every Deployment
These variables define how n8n identifies itself and communicates with the outside world. Configure them correctly from the start.
Core Identity Settings
| Variable | Description | Example |
|---|---|---|
N8N_HOST | The hostname where n8n is accessible | n8n.example.com |
N8N_PORT | Port n8n listens on internally | 5678 |
N8N_PROTOCOL | Protocol for generated URLs | https |
GENERIC_TIMEZONE | Default timezone for schedules | America/New_York |
N8N_HOST determines the hostname in generated URLs. Set this to your actual domain, not localhost, when deploying to a server.
N8N_PROTOCOL affects URL generation throughout the application. If you’re running behind a reverse proxy with SSL termination, set this to https even though n8n itself might listen on HTTP internally.
Webhook Configuration
Webhooks are how external services trigger your workflows. Misconfigure these, and your integrations break silently.
| Variable | Description | Example |
|---|---|---|
WEBHOOK_URL | Public URL for webhook endpoints | https://n8n.example.com/ |
N8N_PROXY_HOPS | Number of reverse proxies in front of n8n | 1 |
WEBHOOK_URL is critical. This URL appears in webhook configuration screens and gets sent to external services. If it doesn’t match your actual public URL, webhooks won’t reach your instance.
# Behind a reverse proxy with SSL
export WEBHOOK_URL=https://n8n.example.com/
export N8N_PROXY_HOPS=1
N8N_PROXY_HOPS tells n8n how many reverse proxies sit between it and the internet. This ensures n8n reads the correct client IP from forwarded headers. Set to 1 for a single proxy (like Nginx or Traefik), 2 if you have a CDN in front of your proxy, and so on.
For detailed webhook security practices, see our webhook security guide.
Security Configuration
Security variables protect your credentials, control access, and prevent unauthorized code execution. Get these wrong, and you risk exposing sensitive data.
N8N_ENCRYPTION_KEY: The Most Critical Variable
This single variable causes more support requests than any other.
n8n encrypts credentials (API keys, passwords, OAuth tokens) before storing them in the database. The encryption key controls this process.
What happens if you don’t set it:
- n8n auto-generates a random key on first launch
- This key gets stored in the
.n8nfolder - If the container restarts and that folder isn’t persisted, the key changes
- All existing credentials become unreadable
- You see: “Credentials could not be decrypted”
The fix:
Always set N8N_ENCRYPTION_KEY explicitly:
# Generate a secure 32-character key
openssl rand -hex 32
# Set it in your environment
export N8N_ENCRYPTION_KEY=your-generated-64-character-hex-string
Queue mode requirement: When running n8n in queue mode with separate workers, every instance (main and workers) must use the exact same encryption key. Workers need to decrypt credentials to execute workflows.
# docker-compose.yml for queue mode
services:
n8n-main:
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
n8n-worker:
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} # Must match!
Store your encryption key securely. Use a secrets manager, keep it in a password vault, back it up somewhere safe. Losing this key means re-creating every credential in your instance.
Code Execution Security
By default, Code nodes in n8n can access environment variables. This creates a security risk if untrusted users can create or modify workflows.
| Variable | Description | Default |
|---|---|---|
N8N_BLOCK_ENV_ACCESS_IN_NODE | Prevent Code nodes from reading env vars | false |
NODES_EXCLUDE | JSON array of nodes to completely disable | [] |
Block environment access:
export N8N_BLOCK_ENV_ACCESS_IN_NODE=true
Disable dangerous nodes entirely:
export NODES_EXCLUDE='["n8n-nodes-base.executeCommand", "n8n-nodes-base.readWriteFile"]'
This blocks the Execute Command and Read/Write Files nodes, preventing workflows from running arbitrary shell commands or accessing the filesystem.
Cookie Security
For web-based authentication, configure cookie security appropriately:
| Variable | Description | Production Value |
|---|---|---|
N8N_SECURE_COOKIE | Require HTTPS for cookies | true |
N8N_SAMESITE_COOKIE | SameSite cookie policy | lax |
Set N8N_SECURE_COOKIE=true when running with HTTPS. This prevents session cookies from being sent over unencrypted connections.
Disabling Telemetry and Templates
n8n collects anonymous usage data by default. For privacy-sensitive deployments:
| Variable | Description | Default |
|---|---|---|
N8N_DIAGNOSTICS_ENABLED | Send anonymous usage data | true |
N8N_VERSION_NOTIFICATIONS_ENABLED | Check for updates | true |
N8N_TEMPLATES_ENABLED | Show workflow templates | true |
# Disable all telemetry
export N8N_DIAGNOSTICS_ENABLED=false
export N8N_VERSION_NOTIFICATIONS_ENABLED=false
export N8N_TEMPLATES_ENABLED=false
Disabling templates removes the template gallery from the UI. This is useful in air-gapped environments or when you want users to only work with internally-approved workflows.
External Storage for Binary Data
When processing files (images, PDFs, spreadsheets), n8n stores binary data. In queue mode with multiple workers, each worker needs access to this data. Configure S3-compatible storage:
| Variable | Description |
|---|---|
N8N_AVAILABLE_BINARY_DATA_MODES | Available storage modes |
N8N_DEFAULT_BINARY_DATA_MODE | Default storage mode |
N8N_EXTERNAL_STORAGE_S3_HOST | S3 endpoint hostname |
N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME | S3 bucket name |
N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION | S3 region |
N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY | S3 access key |
N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET | S3 secret key |
export N8N_AVAILABLE_BINARY_DATA_MODES=filesystem,s3
export N8N_DEFAULT_BINARY_DATA_MODE=s3
export N8N_EXTERNAL_STORAGE_S3_HOST=s3.amazonaws.com
export N8N_EXTERNAL_STORAGE_S3_BUCKET_NAME=my-n8n-binary-data
export N8N_EXTERNAL_STORAGE_S3_BUCKET_REGION=us-east-1
export N8N_EXTERNAL_STORAGE_S3_ACCESS_KEY=${AWS_ACCESS_KEY}
export N8N_EXTERNAL_STORAGE_S3_ACCESS_SECRET=${AWS_SECRET_KEY}
This works with AWS S3, DigitalOcean Spaces, MinIO, and any S3-compatible storage provider. Without shared storage, workers in queue mode cannot access binary data created by other workers, causing workflow failures.
Database Configuration
Production n8n deployments require PostgreSQL. SQLite works for testing but fails under concurrent load and lacks proper backup capabilities. See our guide on common self-hosting mistakes for why this matters.
PostgreSQL Connection
| Variable | Description | Example |
|---|---|---|
DB_TYPE | Database type | postgresdb |
DB_POSTGRESDB_HOST | PostgreSQL server hostname | postgres |
DB_POSTGRESDB_PORT | PostgreSQL port | 5432 |
DB_POSTGRESDB_DATABASE | Database name | n8n |
DB_POSTGRESDB_USER | Database username | n8n_user |
DB_POSTGRESDB_PASSWORD | Database password | Use _FILE suffix |
DB_POSTGRESDB_SCHEMA | Database schema | public |
Complete PostgreSQL configuration:
export DB_TYPE=postgresdb
export DB_POSTGRESDB_HOST=postgres
export DB_POSTGRESDB_PORT=5432
export DB_POSTGRESDB_DATABASE=n8n
export DB_POSTGRESDB_USER=n8n
export DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/db_password
export DB_POSTGRESDB_SCHEMA=n8n
SSL Configuration
For secure database connections (required by most managed PostgreSQL services):
| Variable | Description |
|---|---|
DB_POSTGRESDB_SSL_CA | Path to CA certificate file |
DB_POSTGRESDB_SSL_CERT | Path to client certificate |
DB_POSTGRESDB_SSL_KEY | Path to client key |
DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED | Reject unauthorized certificates |
# For managed PostgreSQL with SSL
export DB_POSTGRESDB_SSL_CA=/path/to/ca-certificate.crt
export DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=true
For a complete PostgreSQL setup tutorial, see our n8n PostgreSQL guide.
Connection Pooling Considerations
When running multiple n8n instances (main plus workers), database connections multiply quickly. Each worker maintains its own connection pool. With 5 workers at the default settings, you might have 50+ connections to PostgreSQL.
Monitor your database connection count and adjust PostgreSQL’s max_connections setting accordingly. For managed database services, check your plan’s connection limits.
Some teams place PgBouncer between n8n and PostgreSQL to manage connection pooling more efficiently. This adds complexity but helps at scale.
Execution and Performance
These variables control how n8n handles workflow executions, manages resources, and maintains performance under load.
Execution Mode
| Variable | Description | Options |
|---|---|---|
EXECUTIONS_MODE | How workflows are executed | regular, queue |
Regular mode (default): The main n8n process handles everything. Simple but doesn’t scale.
Queue mode: Executions are distributed to worker processes via Redis. Required for high-volume deployments. See our queue mode guide for complete setup instructions.
# Enable queue mode
export EXECUTIONS_MODE=queue
Execution Data Management
Execution history accumulates quickly. Without pruning, your database grows indefinitely and queries slow down.
| Variable | Description | Default |
|---|---|---|
EXECUTIONS_DATA_PRUNE | Auto-delete old executions | false |
EXECUTIONS_DATA_MAX_AGE | Hours before pruning | 336 (14 days) |
EXECUTIONS_DATA_PRUNE_MAX_COUNT | Maximum executions to keep | 10000 |
EXECUTIONS_DATA_SAVE_ON_ERROR | Save failed executions | all |
EXECUTIONS_DATA_SAVE_ON_SUCCESS | Save successful executions | all |
EXECUTIONS_DATA_SAVE_ON_PROGRESS | Save in-progress data | false |
EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS | Save manual test runs | true |
Production pruning configuration:
environment:
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=168 # 7 days
- EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
- EXECUTIONS_DATA_SAVE_ON_ERROR=all
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=none # Save storage
- EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
Setting EXECUTIONS_DATA_SAVE_ON_SUCCESS=none significantly reduces database size while preserving failed executions for debugging.
Concurrency Control
| Variable | Description | Default |
|---|---|---|
N8N_CONCURRENCY_PRODUCTION_LIMIT | Max simultaneous executions | -1 (unlimited) |
EXECUTIONS_TIMEOUT | Workflow timeout in seconds | 3600 |
EXECUTIONS_TIMEOUT_MAX | Maximum allowed timeout | 3600 |
Prevent resource exhaustion:
# Limit to 20 concurrent executions
export N8N_CONCURRENCY_PRODUCTION_LIMIT=20
# 30-minute timeout per workflow
export EXECUTIONS_TIMEOUT=1800
For timeout configuration details, see our timeout troubleshooting guide.
Memory Configuration
Node.js has a default memory limit that can cause crashes during heavy operations. Increase it for production:
export NODE_OPTIONS=--max-old-space-size=2048 # 2GB heap
For workflows processing large files or datasets, consider 4096 (4GB) or higher.
| Variable | Description | Default |
|---|---|---|
N8N_PAYLOAD_SIZE_MAX | Maximum request body size in MB | 16 |
N8N_RUNNERS_ENABLED | Enable task runners for isolation | false |
Scaling and Queue Mode Variables
When regular mode can’t handle your volume, queue mode distributes work across multiple processes. This requires Redis and additional configuration.
Redis Connection
| Variable | Description | Example |
|---|---|---|
QUEUE_BULL_REDIS_HOST | Redis server hostname | redis |
QUEUE_BULL_REDIS_PORT | Redis port | 6379 |
QUEUE_BULL_REDIS_PASSWORD | Redis password | Use _FILE suffix |
QUEUE_BULL_REDIS_DB | Redis database number | 0 |
export QUEUE_BULL_REDIS_HOST=redis
export QUEUE_BULL_REDIS_PORT=6379
export QUEUE_BULL_REDIS_PASSWORD_FILE=/run/secrets/redis_password
Worker Configuration
Workers are separate n8n processes that execute workflows from the queue.
| Variable | Description | Default |
|---|---|---|
QUEUE_WORKER_CONCURRENCY | Executions per worker | 10 |
QUEUE_HEALTH_CHECK_ACTIVE | Enable worker health checks | false |
QUEUE_HEALTH_CHECK_PORT | Health check endpoint port | 5678 |
Start a worker:
# Workers need the same encryption key and database config
export N8N_ENCRYPTION_KEY=${MAIN_ENCRYPTION_KEY}
export EXECUTIONS_MODE=queue
export QUEUE_WORKER_CONCURRENCY=10
n8n worker
In Docker Compose:
n8n-worker:
image: docker.n8n.io/n8nio/n8n
command: worker
environment:
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_WORKER_CONCURRENCY=10
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
# ... database variables ...
For scaling strategies and auto-scaling configuration, see our queue mode guide.
Logging and Monitoring
Production deployments need visibility into what’s happening. Configure logging appropriately.
Log Configuration
| Variable | Description | Options |
|---|---|---|
N8N_LOG_LEVEL | Verbosity level | error, warn, info, debug |
N8N_LOG_OUTPUT | Output destinations | console, file |
N8N_LOG_FILE_LOCATION | Log file path | /var/log/n8n/n8n.log |
N8N_LOG_FILE_SIZE_MAX | Max file size in MB | 50 |
N8N_LOG_FILE_COUNT_MAX | Max log files to keep | 60 |
Production logging setup:
export N8N_LOG_LEVEL=info
export N8N_LOG_OUTPUT=console,file
export N8N_LOG_FILE_LOCATION=/var/log/n8n/n8n.log
export N8N_LOG_FILE_SIZE_MAX=50
export N8N_LOG_FILE_COUNT_MAX=30
Use debug level temporarily when troubleshooting, but switch back to info for normal operation. Debug logs are verbose and can fill disks quickly.
Prometheus Metrics
For monitoring dashboards and alerting:
| Variable | Description | Default |
|---|---|---|
N8N_METRICS | Enable metrics endpoint | false |
N8N_METRICS_PREFIX | Metric name prefix | n8n_ |
N8N_METRICS_INCLUDE_DEFAULT_METRICS | Include Node.js metrics | true |
N8N_METRICS_INCLUDE_QUEUE_METRICS | Include queue metrics | false |
export N8N_METRICS=true
export N8N_METRICS_INCLUDE_DEFAULT_METRICS=true
export N8N_METRICS_INCLUDE_QUEUE_METRICS=true
Access metrics at http://your-n8n:5678/metrics.
Complete Production Configuration
Here’s a production-ready Docker Compose configuration incorporating everything we’ve covered.
docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
restart: always
environment:
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: n8n
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: always
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: always
ports:
- "5678:5678"
environment:
# Core
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${N8N_HOST}/
- GENERIC_TIMEZONE=${TIMEZONE}
# Security
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_SECURE_COOKIE=true
- N8N_BLOCK_ENV_ACCESS_IN_NODE=true
# Database
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# Queue Mode
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
# Execution Management
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=168
- EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
- EXECUTIONS_DATA_SAVE_ON_ERROR=all
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
# Performance
- N8N_CONCURRENCY_PRODUCTION_LIMIT=20
- NODE_OPTIONS=--max-old-space-size=2048
# Logging
- N8N_LOG_LEVEL=info
- N8N_LOG_OUTPUT=console
# Metrics
- N8N_METRICS=true
- N8N_METRICS_INCLUDE_QUEUE_METRICS=true
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
n8n-worker:
image: docker.n8n.io/n8nio/n8n:latest
restart: always
command: worker
environment:
# Must match main instance
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
# Database
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# Queue Mode
- EXECUTIONS_MODE=queue
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- QUEUE_BULL_REDIS_PASSWORD=${REDIS_PASSWORD}
- QUEUE_WORKER_CONCURRENCY=10
# Performance
- NODE_OPTIONS=--max-old-space-size=2048
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
postgres_data:
redis_data:
n8n_data:
Environment File (.env)
# Domain Configuration
N8N_HOST=n8n.yourdomain.com
TIMEZONE=America/New_York
# Secrets - Generate these securely!
N8N_ENCRYPTION_KEY=generate-with-openssl-rand-hex-32
POSTGRES_PASSWORD=generate-strong-password-here
REDIS_PASSWORD=generate-another-strong-password
Generate secure values:
# Encryption key (64 hex characters)
openssl rand -hex 32
# Passwords (32 characters)
openssl rand -base64 32
Production Checklist
Before going live, verify:
-
N8N_ENCRYPTION_KEYis set and backed up securely -
WEBHOOK_URLmatches your public domain - Database is PostgreSQL (not SQLite)
- Database credentials use
_FILEsuffix or secure environment -
N8N_SECURE_COOKIE=true(when using HTTPS) - Execution pruning is enabled
- Memory limits are configured appropriately
- Logging is enabled for debugging
- Backups are configured for database and encryption key
For professional setup assistance, see our n8n self-hosted setup service.
Troubleshooting Common Issues
”Credentials could not be decrypted”
Cause: The N8N_ENCRYPTION_KEY changed or wasn’t set consistently.
Solution:
- If you have the original key, restore it in your environment
- If the key is lost, you must re-create all credentials manually
- Prevent this by always setting the key explicitly and backing it up
See our credential management guide for best practices.
Webhooks Not Receiving Data
Cause: WEBHOOK_URL misconfiguration or network issues.
Diagnostic steps:
- Check
WEBHOOK_URLmatches your public URL exactly - Verify
N8N_PROXY_HOPSis set correctly for your proxy setup - Test the webhook URL is accessible from the internet
- Check firewall rules allow inbound traffic on port 443
# Test webhook accessibility
curl -I https://your-n8n-domain.com/webhook-test/test
For OAuth callback issues, see our authentication errors guide.
Memory Errors and Crashes
Cause: Node.js heap exhaustion during large data processing.
Solution:
# Increase heap size
export NODE_OPTIONS=--max-old-space-size=4096
# Reduce concurrent executions
export N8N_CONCURRENCY_PRODUCTION_LIMIT=10
Also consider splitting large workflows into sub-workflows that process data in batches.
Performance Degradation Over Time
Cause: Execution history accumulating without pruning.
Solution:
# Enable automatic pruning
export EXECUTIONS_DATA_PRUNE=true
export EXECUTIONS_DATA_MAX_AGE=168
export EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
# Stop saving successful executions (optional)
export EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
For persistent performance issues, consider migrating to queue mode with dedicated workers.
Container Restarts and Lost Data
Symptom: After a container restart, settings or workflows appear missing.
Cause: Data volumes not properly mounted or persisted.
Solution:
- Verify volume mounts in docker-compose.yml point to persistent storage
- Check that the n8n data directory (
/home/node/.n8n) is mounted - For Kubernetes, ensure PersistentVolumeClaims are bound correctly
volumes:
- n8n_data:/home/node/.n8n # Critical for persistence
The .n8n folder contains the auto-generated encryption key (if not set via environment), configuration, and other state. Losing this folder without proper environment variables means losing access to credentials.
Workers Not Processing Jobs
Cause: Queue mode misconfiguration.
Checklist:
- Both main and workers have
EXECUTIONS_MODE=queue - Redis connection details are identical across all instances
N8N_ENCRYPTION_KEYmatches between main and workers- Database connection is accessible from workers
- Workers are running (check with
docker ps)
Use our workflow debugger tool to analyze execution patterns.
Frequently Asked Questions
What happens if I lose my N8N_ENCRYPTION_KEY?
If you lose your encryption key, all stored credentials become permanently unreadable. There is no recovery option. You must:
- Create a new encryption key
- Delete the corrupted credentials from the database
- Re-create every credential manually (re-authenticate OAuth connections, re-enter API keys)
This is why backing up your encryption key is critical. Store it in a password manager, secrets vault, or secure backup location separate from your database backups.
How do I migrate environment variables when updating n8n?
Environment variables don’t need migration between n8n versions. They’re external to the application. However:
- Before updating: Document your current configuration
- Keep the same encryption key: Never change
N8N_ENCRYPTION_KEYduring updates - Use specific version tags: Instead of
latest, use explicit versions like1.67.1 - Test in staging first: Verify the new version works before production
# Good practice: explicit versions
image: docker.n8n.io/n8nio/n8n:1.67.1
# Risky: version could change unexpectedly
image: docker.n8n.io/n8nio/n8n:latest
Can I use different environment variables for dev and production?
Yes, and you should. Use separate .env files or environment-specific configurations:
# .env.development
N8N_HOST=localhost
N8N_PROTOCOL=http
WEBHOOK_URL=http://localhost:5678/
N8N_LOG_LEVEL=debug
# .env.production
N8N_HOST=n8n.company.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.company.com/
N8N_LOG_LEVEL=info
In Docker Compose, use the --env-file flag:
docker compose --env-file .env.production up -d
Note that credentials and variable values must be set manually in each environment. n8n’s source control feature syncs workflow definitions but not secrets.
Why are my webhooks not working after deployment?
The most common causes:
- WEBHOOK_URL mismatch: The URL in n8n doesn’t match your public domain
- Missing SSL: External services often require HTTPS webhooks
- Firewall blocking: Inbound traffic isn’t reaching port 443
- Proxy misconfiguration:
N8N_PROXY_HOPSisn’t set correctly
Debug by testing the webhook URL externally:
curl -X POST https://your-domain.com/webhook-test/test \
-H "Content-Type: application/json" \
-d '{"test": "data"}'
If this fails, the issue is network/proxy configuration. If it succeeds but external services can’t reach you, check their specific requirements (IP whitelisting, SSL certificate validation).
How do I configure n8n behind a reverse proxy?
Set these variables:
export WEBHOOK_URL=https://your-public-domain.com/
export N8N_HOST=your-public-domain.com
export N8N_PROTOCOL=https
export N8N_PROXY_HOPS=1
Configure your reverse proxy (Traefik, Nginx, Caddy) to:
- Forward traffic to n8n’s internal port (5678)
- Handle SSL termination
- Pass original client headers (X-Forwarded-For, X-Forwarded-Proto)
Example Nginx configuration:
server {
listen 443 ssl;
server_name n8n.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
For complete Docker deployment with automatic SSL via Let’s Encrypt, see our Docker setup guide.
Next Steps
Proper configuration is the foundation of a reliable n8n deployment. With the right environment variables in place, you can focus on building workflows instead of debugging infrastructure.
If you’re setting up a new deployment, start with our Docker setup guide. For scaling existing installations, read the queue mode guide.
Need help configuring your n8n instance? Our consulting services include configuration audits and production optimization.
Quick Reference: Environment Variable Cheatsheet
Here’s a condensed reference of the most important variables for quick lookup.
Must-Have Variables
# Always set these explicitly
N8N_ENCRYPTION_KEY=<your-64-char-hex-key>
WEBHOOK_URL=https://your-domain.com/
N8N_HOST=your-domain.com
N8N_PROTOCOL=https
DB_TYPE=postgresdb
Production Defaults
# Recommended production settings
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
N8N_CONCURRENCY_PRODUCTION_LIMIT=20
NODE_OPTIONS=--max-old-space-size=2048
N8N_SECURE_COOKIE=true
N8N_BLOCK_ENV_ACCESS_IN_NODE=true
N8N_LOG_LEVEL=info
Queue Mode Essentials
# Required for queue mode
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=redis
QUEUE_BULL_REDIS_PORT=6379
QUEUE_WORKER_CONCURRENCY=10
For the complete official reference, see the n8n environment variables documentation.