N8N_ENCRYPTION_KEY must be set before your first production boot. Without it, stored credentials become undecryptable after restarts - and there's no recovery path. This single variable causes more support tickets than any other n8n configuration issue.
The official docs scatter environment variables across multiple pages (deployment, nodes, endpoints). This reference consolidates everything into one place with production-ready defaults.
How Precedence Works
n8n reads configuration in this order:
- Process environment variables (highest priority)
- Config files
- Built-in defaults (lowest priority)
If you set both VAR and VAR_FILE, the file value wins. Use the _FILE suffix for secrets from mounted volumes or secret managers - keeps plaintext out of your docker-compose.yml (source).
Must-Set Before First Run
These variables cannot be safely changed after your instance has stored data:
| Variable | Why It Matters |
|---|---|
N8N_ENCRYPTION_KEY | Encrypts all stored credentials. Change it later = lost access to every saved credential. |
N8N_ENCRYPTION_KEY_FILE | Alternative: path to file containing the key. Preferred for container deployments. |
DB_TYPE | Switching databases after workflows exist requires manual migration. |
Set N8N_ENCRYPTION_KEY or N8N_ENCRYPTION_KEY_FILE before first prod boot - official docs and community cheat sheets both emphasize this as the single most critical configuration step (source).
Categorized Reference Table
Database Configuration
| Variable | Default | Description |
|---|---|---|
DB_TYPE | sqlite | Options: sqlite, postgresdb, mysqldb, mariadb |
DB_POSTGRESDB_HOST | localhost | PostgreSQL host |
DB_POSTGRESDB_PORT | 5432 | PostgreSQL port |
DB_POSTGRESDB_DATABASE | n8n | Database name |
DB_POSTGRESDB_USER | root | Database user |
DB_POSTGRESDB_PASSWORD | - | Database password |
DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED | true | Set to false for self-signed certs |
Security & Authentication
| Variable | Default | Description |
|---|---|---|
N8N_ENCRYPTION_KEY | - | Required for production. 32+ character random string. |
N8N_BASIC_AUTH_ACTIVE | false | Enable basic auth for UI |
N8N_BASIC_AUTH_USER | - | Basic auth username |
N8N_BASIC_AUTH_PASSWORD | - | Basic auth password |
N8N_BLOCK_ENV_ACCESS_IN_NODE | false | Prevents workflows from reading env vars. Set true on shared instances. |
Small teams use N8N_BASIC_AUTH_ACTIVE=true with username/password to gate UI access. For shared installs, N8N_BLOCK_ENV_ACCESS_IN_NODE=true prevents workflows from leaking secrets (source).
Webhooks & Endpoints
| Variable | Default | Description |
|---|---|---|
N8N_HOST | localhost | Internal host n8n listens on |
N8N_PORT | 5678 | Internal port |
N8N_PROTOCOL | http | Set to https when using SSL |
WEBHOOK_URL | - | Public URL for webhooks. Critical behind reverse proxies. |
N8N_EDITOR_BASE_URL | - | Public URL for the editor UI |
N8N_PAYLOAD_SIZE_MAX | 16 | Max webhook payload in MiB |
N8N_FORMDATA_FILE_SIZE_MAX | 200 | Max form-data file upload in MiB |
Executions & Performance
| Variable | Default | Description |
|---|---|---|
EXECUTIONS_MODE | regular | Options: regular, queue (for scaling) |
EXECUTIONS_TIMEOUT | -1 | Timeout in seconds. -1 = no timeout. |
EXECUTIONS_TIMEOUT_MAX | 3600 | Max allowed timeout |
EXECUTIONS_DATA_SAVE_ON_ERROR | all | Save execution data on errors |
EXECUTIONS_DATA_SAVE_ON_SUCCESS | all | Save execution data on success |
EXECUTIONS_DATA_PRUNE | false | Auto-delete old executions |
EXECUTIONS_DATA_MAX_AGE | 336 | Hours to keep executions (if pruning enabled) |
SMTP & Email
| Variable | Default | Description |
|---|---|---|
N8N_EMAIL_MODE | smtp | Email sending mode |
N8N_SMTP_HOST | - | SMTP server hostname |
N8N_SMTP_PORT | 465 | SMTP port |
N8N_SMTP_USER | - | SMTP username |
N8N_SMTP_PASS | - | SMTP password |
N8N_SMTP_SSL | true | Use SSL for SMTP |
N8N_SMTP_SENDER | - | From address for emails |
Logging & Debugging
| Variable | Default | Description |
|---|---|---|
N8N_LOG_LEVEL | info | Options: error, warn, info, verbose, debug |
N8N_LOG_OUTPUT | console | Options: console, file |
N8N_LOG_FILE_LOCATION | - | Path for log file (if output=file) |
Nodes Configuration
| Variable | Default | Description |
|---|---|---|
NODES_INCLUDE | - | Comma-separated list of nodes to load (whitelist) |
NODES_EXCLUDE | - | Comma-separated list of nodes to block |
Production deployments often exclude high-risk nodes: NODES_EXCLUDE=n8n-nodes-base.executeCommand,n8n-nodes-base.ssh removes shell access entirely (source).
Network & Proxy
| Variable | Default | Description |
|---|---|---|
HTTP_PROXY | - | Proxy for outbound HTTP requests |
HTTPS_PROXY | - | Proxy for outbound HTTPS requests |
NO_PROXY | - | Comma-separated hosts to bypass proxy |
N8N_SSL_KEY | - | Path to SSL private key |
N8N_SSL_CERT | - | Path to SSL certificate |
Behind corporate firewalls, missing proxy vars cause nodes to fail on external API calls silently.
N8N_HOST vs WEBHOOK_URL - The Critical Difference
This trips up most first-time self-hosters:
N8N_HOST and N8N_PORT control what n8n listens on internally. Default: localhost:5678.
WEBHOOK_URL (or N8N_EDITOR_BASE_URL) defines the public external URL that appears in webhook URLs and email links.
When you run n8n behind nginx or Traefik:
-
n8n listens on
localhost:5678internally -
Your reverse proxy handles SSL termination on
https://n8n.yourdomain.com -
Webhooks need to use the public URL, not localhost
Without WEBHOOK_URL set correctly, your workflow webhook URLs will contain localhost:5678 - which external services obviously cannot reach (source).
Common Misconfigurations
Skipping N8N_PROTOCOL=https: Your instance runs but browsers show security warnings, and some integrations reject non-HTTPS webhook URLs.
Empty NODES_EXCLUDE: Exposes nodes like executeCommand that can run arbitrary shell commands. On shared instances, this is a security hole.
Missing HTTP_PROXY behind corporate networks: Nodes that call external APIs fail silently or timeout. Debug logs show connection refused.
Setting N8N_ENCRYPTION_KEY after first boot: Existing credentials become unreadable. You'll need to re-enter every stored credential manually.
Wrong WEBHOOK_URL with reverse proxy: Webhooks show internal URLs. External services can't reach them. Symptoms: workflows never trigger from external events.
Production .env Template
Copy this as a starting point. Replace placeholder values:
# ===================
# CRITICAL - SET FIRST
# ===================
N8N_ENCRYPTION_KEY=your-32-character-random-string-here
# ===================
# DATABASE
# ===================
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=localhost
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=your-db-password
# ===================
# NETWORK
# ===================
N8N_HOST=0.0.0.0
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com
N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com
# ===================
# SECURITY
# ===================
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=your-strong-password
N8N_BLOCK_ENV_ACCESS_IN_NODE=true
NODES_EXCLUDE=n8n-nodes-base.executeCommand
# ===================
# EXECUTIONS
# ===================
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
EXECUTIONS_TIMEOUT=300
# ===================
# SMTP (optional)
# ===================
N8N_SMTP_HOST=smtp.yourdomain.com
N8N_SMTP_PORT=587
N8N_SMTP_USER=notifications@yourdomain.com
N8N_SMTP_PASS=smtp-password
N8N_SMTP_SENDER=n8n@yourdomain.com
# ===================
# LOGGING
# ===================
N8N_LOG_LEVEL=info
Generate your encryption key with: openssl rand -hex 32
FAQ
What happens if I change N8N_ENCRYPTION_KEY after storing credentials?
All existing credentials become unreadable. n8n cannot decrypt them with a different key. You'll need to delete and re-create every credential. There's no recovery mechanism.
Do I need WEBHOOK_URL if I'm not using a reverse proxy?
If n8n is directly accessible on its public IP/port, you still want WEBHOOK_URL set to that public address. Otherwise webhook URLs will contain whatever N8N_HOST is set to (often localhost or 0.0.0.0).
Should I use NODES_INCLUDE or NODES_EXCLUDE?
NODES_EXCLUDE is safer for most cases - you block specific risky nodes while keeping access to everything else. NODES_INCLUDE is a whitelist approach useful when you want to lock down to a very specific set of allowed nodes.
How do I use the _FILE suffix for secrets?
Instead of N8N_ENCRYPTION_KEY=mysecret, use N8N_ENCRYPTION_KEY_FILE=/run/secrets/n8n_key and mount a file containing just the secret value. Works with Docker secrets and Kubernetes secrets.
Why aren't my webhooks triggering from external services?
Check WEBHOOK_URL. If it's not set or points to localhost, external services can't reach your n8n instance. The webhook URL in your workflow should match your public domain.
Need help configuring n8n for production? n8n Logic specializes in n8n deployments - from initial setup to complex workflow automation. We handle the infrastructure so you can focus on building.