11 min read

n8n Encryption Key: Complete Guide to N8N_ENCRYPTION_KEY, Credential Security & Recovery

Hero image for n8n Encryption Key: Complete Guide to N8N_ENCRYPTION_KEY, Credential Security & Recovery

💡

Your n8n credentials are only as safe as your N8N_ENCRYPTION_KEY. Treat it like production database root access guard it, back it up, and test recovery.

Encryption key basics

What you’ll learn:

  • How N8N_ENCRYPTION_KEY protects credentials
  • Why the same key must persist
  • Where decryption happens in n8n

Losing the key means losing every stored secret. Keeping it safe keeps your automation alive.

Think of it as a master safe combination. If it changes or disappears, the contents stay locked.

How it works

  • Encrypts at rest: N8N_ENCRYPTION_KEY encrypts sensitive fields before storage
  • Decrypts in memory: n8n decrypts only at run time in process memory, then calls the node
  • Persists across nodes: Self-hosted deployments must supply the same key across restarts and replicas

What the key does

  • Protects secrets: API keys, tokens, passwords stored in the database
  • Must match everywhere: Identical key for main process and all workers
  • Rotation is explicit: Rotation requires a deliberate re-encryption step

If credentials fail to decrypt after a restart, the running key likely differs from the one used to encrypt.

Cloud vs self-hosted

DeploymentKey ownerYour focus
n8n CloudProviderApp hardening and backups
Self-hostedYouKey generation, storage, backups, rotation

One rule holds everywhere: keep the same key for the lifetime of the credentials.

flowchart TD
    A[Store secret] --> B[Encrypt with key]
    B --> C[Save to DB]
    D[Run workflow] --> E[Load secret]
    E --> F[Decrypt in memory]
    F --> G[Call service]

    classDef trigger fill:#e1f5fe,stroke:#01579b
    classDef process fill:#fff3e0,stroke:#ef6c00
    classDef action fill:#e8f5e8,stroke:#2e7d32

    class A,D trigger
    class B,E,F process
    class C,G action

With the fundamentals clear, let’s set up a strong key and deliver it safely.


Setup guides

What you’ll learn:

  • How to generate a strong key
  • How to configure Docker, systemd, Kubernetes
  • How to run queue mode with the same key

Generate a strong key

# 32 bytes base64 (~256-bit)
openssl rand -base64 32 > n8n.key
chmod 600 n8n.key
  • Store in a vault: Keep n8n.key in a secrets manager or secure store
  • Avoid shell leaks: Write to files with 0600 permissions to prevent history exposure
  • Prefer managed KMS: A key management service (KMS) or hardware-backed HSM (hardware security module) improves security

Consistency matters more than cleverness. Pick a format and stick to it.

Docker compose

# .env (chmod 600)
N8N_ENCRYPTION_KEY=$(cat n8n.key)
N8N_PORT=5678
# docker-compose.yml
version: "3.9"
services:
  n8n:
    image: n8nio/n8n:latest
    env_file: [.env]
    environment:
      - TZ=UTC
    ports:
      - "5678:${N8N_PORT}"
    volumes:
      - n8n_data:/home/node/.n8n
      - n8n_local_files:/files
    restart: unless-stopped
volumes:
  n8n_data:
  n8n_local_files:
  • Persist data dir: Keep /home/node/.n8n to avoid accidental key regeneration on rebuilds
  • Keep secrets out of Git: Inject via CI or secrets manager
  • Use Docker secrets: For Swarm, prefer secrets over env vars

Compose first, then promote to Swarm or Kubernetes once secrets handling is solid.

Optional: Swarm secret

echo -n "$(cat n8n.key)" | docker secret create n8n_encryption_key -
# snippet inside the service
secrets:
  - source: n8n_encryption_key
    target: n8n_encryption_key
    uid: "1000"
    mode: 0400
environment:
  - N8N_ENCRYPTION_KEY_FILE=/run/secrets/n8n_encryption_key

Reading from a file keeps the key out of env inspection tools.

Linux service (systemd)

# /etc/n8n/n8n.env (chmod 600)
N8N_ENCRYPTION_KEY=$(cat /root/n8n.key)
# /etc/systemd/system/n8n.service
[Unit]
Description=n8n Automation Server
After=network.target

[Service]
User=n8n
WorkingDirectory=/opt/n8n
EnvironmentFile=/etc/n8n/n8n.env
ExecStart=/usr/local/bin/n8n start
Restart=on-failure

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now n8n
sudo systemctl status n8n
  • Keep env in root-owned file: Do not put secrets in unit files
  • Restrict read access: Use least privilege
  • Scrub logs: Do not echo the key anywhere

Kubernetes (Secrets and Deployment)

kubectl create secret generic n8n-encryption \
  --from-literal=N8N_ENCRYPTION_KEY="$(cat n8n.key)"
apiVersion: apps/v1
kind: Deployment
metadata:
  name: n8n
spec:
  replicas: 1
  selector: { matchLabels: { app: n8n } }
  template:
    metadata: { labels: { app: n8n } }
    spec:
      containers:
        - name: n8n
          image: n8nio/n8n:latest
          env:
            - name: N8N_ENCRYPTION_KEY
              valueFrom:
                secretKeyRef:
                  name: n8n-encryption
                  key: N8N_ENCRYPTION_KEY
          volumeMounts:
            - name: n8n-data
              mountPath: /home/node/.n8n
      volumes:
        - name: n8n-data
          persistentVolumeClaim:
            claimName: n8n-pvc
  • Use Secrets: Never store the key in a ConfigMap
  • Encrypt etcd: Enable Secret encryption at rest
  • Limit access: Use RBAC (role-based access control) to restrict Secret reads

Queue mode (main and workers)

# Main (API and UI)
export N8N_ENCRYPTION_KEY="$(cat n8n.key)"
export N8N_EXECUTIONS_MODE=queue
n8n start
# Worker processes same key required
export N8N_ENCRYPTION_KEY="$(cat n8n.key)"
export N8N_EXECUTIONS_MODE=queue
n8n worker
  • Same key everywhere: All processes must share the same key
  • Mismatch breaks runs: Decryption failures surface on worker execution
  • Keep workers stateless: Mount only what is required
flowchart TD
    A[Choose setup] --> B[Docker]
    A --> C[Systemd]
    A --> D[Kubernetes]
    B --> E[Env file]
    B --> F[Swarm secret]
    C --> G[Env file]
    D --> H[Cluster Secret]

    classDef trigger fill:#e1f5fe,stroke:#01579b
    classDef process fill:#fff3e0,stroke:#ef6c00

    class A trigger
    class B,C,D,E,F,G,H process

With your key delivered safely, reduce risk with secure storage and backups.


Security practices

What you’ll learn:

  • How to store and back up the key
  • How to deliver secrets with lower leak risk
  • What mistakes to avoid

Store and back up the key

  • Use a secrets manager: Prefer a managed KMS or HSM-backed store
  • Keep two offline backups: Separate regions or facilities
  • Document break-glass steps: Owners, locations, and procedures

A secret that cannot be recovered is downtime waiting to happen.

Secret delivery patterns

PatternLocationTradeoff
Env varFile on diskSimple but easy to leak via logs and tools
File mountIn-memory fsAvoids env introspection, must manage permissions
KMS bootstrapExternal KMSCentral rotation and audit with added dependency

Prefer file-based or KMS bootstrap for production.

Examples: external managers

AWS Secrets Manager to env file at start:

aws secretsmanager get-secret-value \
  --secret-id n8n/encryption-key \
  --query SecretString --output text > /etc/n8n/n8n.key
chmod 600 /etc/n8n/n8n.key
export N8N_ENCRYPTION_KEY="$(cat /etc/n8n/n8n.key)"

HashiCorp Vault with agent template to file:

# /etc/vault.d/n8n.hcl
exit_after_auth = false
auto_auth { method "approle" { mount_path = "auth/approle" role_id = "..." secret_id_file = "/etc/vault.d/secret_id" } }
sinks { file { path = "/run/vault/token" } }
# Template renders key to a file with 0400
consul-template -template \
  "/etc/templates/n8n.ctmpl:/run/secrets/N8N_ENCRYPTION_KEY:0400" \
  -once

Centralized control makes rotation and audit tractable.

Avoid common mistakes

  • Do not commit keys: Never push keys to Git, even private repos
  • Do not log env: Avoid printing environment at startup
  • Do not drop data volumes: Rebuilds must keep persistent data
  • Do not mix keys: Never run multiple keys against one database
💡

Tip: add a preflight that halts startup if the expected key source is missing or the data directory is not mounted.

Now that storage is solid, prepare for recovery before you need it.


Recovery playbook

What you’ll learn:

  • How to diagnose decryption errors
  • How to restore or rebuild safely
  • How to migrate and drill

Diagnose decryption failures

# 1) Check the active key source
printenv | grep -E '^N8N_ENCRYPTION_KEY'
ls -l /home/node/.n8n || true

# 2) Inspect recent deploys for key changes
sudo ls -lt /etc/n8n
sudo git -C /infra/env-config log -n 3 -- .

# 3) Look for decryption errors
journalctl -u n8n --since "-2h" | grep -i decrypt || true
  • Restore the old key if it differs from last known good
  • Check mounts first if the key vanished
  • Document actions to speed future fixes
flowchart TD
    A[Decrypt errors] --> B[Have old key]
    B -->|Yes| C[Restore key]
    B -->|No| D[Rebuild creds]
    C --> E[Test workflows]
    D --> F[Rotate at providers]
    E --> G[Fix process gap]

    classDef alert fill:#f3e5f5,stroke:#7b1fa2
    classDef action fill:#e8f5e8,stroke:#2e7d32

    class A alert
    class B alert
    class C,E,G,F action

You have the old key

# Stop services
docker compose down || true
systemctl stop n8n || true

# Restore the known-good key
cp /secure-backups/n8n.key /etc/n8n/n8n.key
chmod 600 /etc/n8n/n8n.key

# Start services
systemctl start n8n || docker compose up -d
  • Validate on staging when possible
  • Run critical workflows end to end
  • Capture a post-mortem for process improvements

You do not have the key

  • Crypto is working: n8n cannot decrypt without the original key
  • Re-create credentials: Assign new credential identifiers
  • Rotate providers: Use provider consoles to rotate API keys and OAuth tokens
# Triage worksheet (CSV)
printf "workflow_id,credential_name,owner,provider,rotation_status\n" > triage.csv

Accept the loss quickly to minimize downtime and risk.

Safe migration to new servers

# 1) Freeze writes to old instance
systemctl stop n8n || docker compose down

# 2) Back up database and data dir
pg_dump -h db -U n8n n8n > n8n.sql
 tar czf n8n_home.tgz -C /home/node .n8n

# 3) Restore on target before first start
scp n8n.sql target:
scp n8n_home.tgz target:
psql -h db -U n8n -d n8n < n8n.sql
 tar xzf n8n_home.tgz -C /home/node
export N8N_ENCRYPTION_KEY="$(cat /home/node/.n8n/key || cat /etc/n8n/n8n.key)"

# 4) Start and test
n8n start
  • Do not start without the original key in place
  • Validate decryption on a non-production clone
  • Cut over with DNS after tests pass

Practice a quarterly drill

#!/usr/bin/env bash
set -euo pipefail
# Recover into ephemeral env and run smoke tests
EXPORT_KEY=$(cat /secure-backups/n8n.key)
export N8N_ENCRYPTION_KEY="$EXPORT_KEY"
docker compose -f docker-compose.test.yml up -d
sleep 10
curl -fsS http://localhost:5679/rest/healthz >/dev/null
printf "[%s] DR drill OK\n" "$(date)" >> recovery.log
  • Schedule the drill and track results
  • Fail the build if the drill fails
  • Keep recent artifacts for faster audits

Case study and checklists

What you’ll learn:

  • A real outage timeline and fix
  • Hardening steps before production
  • Operations and rotation practices

Background and timeline

  1. Monday 02:00 host patched, containers rebuilt from scratch
  2. 02:05 data volume missing, n8n generated a fresh key silently
  3. 08:10 decryption errors reported in alerts

One missing volume mount became a full morning incident.

Symptoms and investigation

  • Workflow failures: First node using credentials failed
  • Visible but locked: Credentials list visible but unusable
  • Log evidence: Repeated decryption errors after redeploy window

Root cause was a changed compose file without the data volume.

Recovery actions

# Stop prod and clone to staging to test recovery
docker compose down

# Mount the correct volume and restore the last known-good key
docker volume create n8n_data
docker run --rm -v n8n_data:/data -v /secure-backups:/bk alpine \
  sh -c 'cd /data && tar xzf /bk/n8n_home_2025-11-30.tgz'

# Start with the original key injected
export N8N_ENCRYPTION_KEY="$(cat /secure-backups/n8n.key)"
docker compose up -d

# Smoke test critical workflows
n8n execute --id 12 --raw
n8n execute --id 34 --raw
  • Validated in staging to avoid compounding risk
  • Repeated steps in production after checks passed
  • MTTR under 90 minutes after diagnosis

Lessons learned

  • Key in vault and volume mounted with guardrails
  • CI checks prevent merges that drop persistence
  • Startup preflight blocks boot without the expected key source
erDiagram
    Workflow ||--o{ Execution : runs
    Workflow }o--o{ Credential : uses

    Workflow {
        int id
        string name
        datetime created_at
    }
    Credential {
        int id
        string name
        string data_enc
        datetime created_at
    }
    Execution {
        int id
        int workflow_id
        datetime created_at
    }

Checklists

Pre-production hardening

  • Generate a 256-bit key and store it in a vault
  • Deliver secrets by file or KMS with 0400 permissions
  • Persist /home/node/.n8n across restarts
  • Block logging of environment and startup config
  • Document break-glass owners and backup locations

Operations and rotation

  • Drill recovery quarterly in a clean environment
  • Alert on container restarts without the expected volume
  • Rotate provider API keys on a schedule and re-encrypt as needed
  • Restrict Secrets read permissions with least privilege
  • Test queue workers with the same key before scaling out

Next steps

  • Adopt a central secrets manager if you have not already
  • Automate a preflight that fails boot without the key
  • Add a migration runbook with explicit ordering

Keep the playbook near the keyboard, not buried in a wiki.

💡

Bottom line: persist one strong key, deliver it securely, and rehearse recovery. Do that and your n8n credentials will stay safe even on your worst day.

📧