Who this is for: n8n operators, DevOps engineers, and security‑focused developers who run n8n in Docker, Kubernetes, or on‑premise servers and need to keep API keys, DB passwords, and OAuth tokens out of logs, process listings, and container metadata. We cover this in detail in the n8n Security & Hardening Guide.
Quick Diagnosis
If API keys, database passwords, or OAuth tokens appear in logs, container metadata, or ps output, you are most likely leaking environment‑variable secrets. The fastest remediation is to stop storing production secrets directly in plain‑text env vars and move them to a secret‑management solution (Docker/Kubernetes secrets, OS vault, or encrypted .env).
Why Environment Variables Leak in n8n ?
If you encounter any default credentials vulnerability resolve them before continuing with the setup.
Common Leak Vectors
| Vector | Typical Impact |
|---|---|
Process list (ps aux) – n8n inherits all env vars |
Immediate exposure of API keys, DB passwords |
Docker inspect / docker‑compose config – env vars stored in container definition |
Secrets visible to anyone with Docker access |
Kubernetes describe pod / env – env vars in pod spec |
Cluster‑wide breach if RBAC is weak |
Log files – accidental console.log(process.env) |
Persistent secret copies in log storage |
| CI/CD pipeline prints – debug mode echoes env vars | Secrets leak to build logs, possibly public |
EEFA note: The process list is the most overlooked vector. Even with restricted Docker commands, a compromised container can read its own environment via
cat /proc/$$/environ.
How n8n Loads Secrets ?
- Bootstrap – The Docker entrypoint runs
node packages/cli/src/cli.js. - Config loader –
src/cli.tscallsdotenv.config()(if a.envfile exists) and mergesprocess.envinto the NodeConfig object. - Runtime – Nodes retrieve credentials with
this.getNodeParameter('apiKey'), which resolves toprocess.env.N8N_API_KEYwhen the parameter is set to “environment variable”.
Credential resolver snippet
// Resolve env‑based credential
if (credentialValue.startsWith('env:')) {
const envKey = credentialValue.replace(/^env:/, '');
const secret = process.env[envKey];
if (!secret) throw new Error(`Missing env var ${envKey}`);
return secret;
}
EEFA warning: The error message reveals the missing variable name, unintentionally disclosing which secret is expected.
Real‑World Exposure Scenarios
If you encounter any insecure webhook exposure resolve them before continuing with the setup.
| Scenario | Symptoms | Root Cause |
|---|---|---|
Accidental docker logs dump |
Log lines contain Authorization: Bearer <token> |
Custom webhook node logs the full request object |
| CI pipeline prints env | Build log shows N8N_DB_PASSWORD=***** |
CI step uses set -x (bash debug) |
| Kubernetes pod crash | Error: ENOENT … open '/run/secrets/N8N_API_KEY' |
Secret volume not mounted; fallback to empty env var |
Docker Compose --force-recreate |
New containers start with old env vars | Hard‑coded environment: entries remain in compose file |
Secure Storage Options
1. Docker Secrets (Swarm)
Store each secret as a read‑only file inside the container and avoid exposing it via environment:.
Create a secret
# Example: DB password echo "SuperSecretPass123!" | docker secret create n8n_db_password -
Reference in docker‑compose.yml
services:
n8n:
image: n8nio/n8n
secrets:
- n8n_db_password
environment:
- DB_PASSWORD_FILE=/run/secrets/n8n_db_password
secrets:
n8n_db_password:
external: true
EEFA note: Docker secrets are mounted as read‑only files. Never duplicate the value in
environment:.
2. Kubernetes Secrets
Mount secrets as environment variables or files.
Define the secret
apiVersion: v1 kind: Secret metadata: name: n8n-api-key type: Opaque stringData: N8N_API_KEY: "k8s-super-secret-key"
Use in a deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
spec:
template:
spec:
containers:
- name: n8n
image: n8nio/n8n
envFrom:
- secretRef:
name: n8n-api-key
EEFA warning: Kubernetes secrets are base64‑encoded, not encrypted. Enable
EncryptionConfigurationon the API server for true at‑rest encryption.
3. OS‑Level Secret Stores
Leverage native secret managers (Linux pass, macOS Keychain, Windows Credential Manager) and inject values at runtime.
Bash wrapper example (Linux pass)
#!/usr/bin/env bash # fetch-secret.sh – injects N8N_API_KEY from `pass` export N8N_API_KEY=$(pass show n8n/api-key) exec "$@"
Run n8n with: ./fetch-secret.sh node packages/cli/src/cli.js start.
4. Encrypted .env Files (last resort)
| Tool | Encryption | Integration |
|---|---|---|
| sops | GPG/PGP or cloud KMS | sops -e .env > .env.enc; decrypt in entrypoint script |
| dotenv‑vault | HashiCorp Vault | vault kv get -field=value secret/n8n/api-key > .env then dotenv -e .env node … |
EEFA tip: Keep the decryption key separate from the image layer; use an init side‑car that fetches the key from a vault. If you encounter any jwt auth misconfiguration resolve them before continuing with the setup.
Step‑by‑Step Migration: .env → Docker Secrets
1. List current secrets
grep -E '^N8N_' .env # Example output: # N8N_DB_PASSWORD=SuperSecret # N8N_API_KEY=abcd1234
2. Create Docker secrets
# DB password cat .env | grep '^N8N_DB_PASSWORD=' | cut -d= -f2- | docker secret create n8n_db_password - # API key cat .env | grep '^N8N_API_KEY=' | cut -d= -f2- | docker secret create n8n_api_key -
3. Update docker‑compose.yml
services:
n8n:
image: n8nio/n8n
secrets:
- n8n_db_password
- n8n_api_key
environment:
- DB_PASSWORD_FILE=/run/secrets/n8n_db_password
- API_KEY_FILE=/run/secrets/n8n_api_key
command: >
sh -c "export DB_PASSWORD=$(cat $DB_PASSWORD_FILE) &&
export N8N_API_KEY=$(cat $API_KEY_FILE) &&
node packages/cli/src/cli.js start"
secrets:
n8n_db_password:
external: true
n8n_api_key:
external: true
4. Clean up
- Remove secret entries from
.env. - Add a
.env.examplewith non‑secret defaults for developers.
5. Deploy
docker stack deploy -c docker-compose.yml n8n_stack
6. Verify no leakage
docker exec -it $(docker ps -q -f "name=n8n") env | grep N8N_ # Should output nothing because secrets are read from files, not env.
Checklist: Confirm No Secret Leakage
| Item | Verification Command |
|---|---|
| Process list | ps aux | grep node – ensure no N8N_ vars appear |
| Docker inspect | docker inspect <container> | grep -i env – no secret entries |
| Kubernetes pod env | kubectl exec <pod> -- env | grep N8N_ – only non‑secret vars |
| Log sanitization | Review custom node code; mask process.env before logging |
| CI/CD hygiene | Disable set -x; enable secret masking in CI platform |
| Secret rotation | Update a Docker secret and confirm n8n picks up the new value (via file‑watch script) |
EEFA final advice: Secret leakage is often discovered after an incident. Apply defense‑in‑depth: combine Docker/K8s secrets with runtime checks (the checklist) and audit every CI/CD job that interacts with n8n.
This page is part of the comprehensive “n8n security guide”. For a high‑level overview, visit the n8n security guide.



