n8nen.nl logo n8nen.nl

N8N Workflow Variables & Environment Config: Complete Secrets Management Guide (2025)

2025-01-29 Sam van N8Nen.nl
N8N Workflow Variables & Environment Config: Complete Secrets Management Guide (2025)

🔐 Samenvatting

N8N biedt krachtige mogelijkheden voor het beheren van variables, environment configuration en secrets. Deze gids laat zien hoe je workflow variables gebruikt, environment files configureert, externe secrets integreert met HashiCorp Vault of AWS Secrets Manager, en best practices implementeert voor secure credential management. Perfect voor teams die werken met dev/staging/production environments.

Inhoudsopgave

Waarom Variables & Secrets Management?

Hardcoded credentials en configuration in workflows zijn een security nightmare. Variables en secrets management lossen deze problemen op:

❌ Zonder Proper Management

  • • API keys zichtbaar in workflows
  • • Passwords in version control
  • • Manual changes per environment
  • • Geen audit trail
  • • Security breaches risico
  • • Compliance issues

✅ Met Variables & Secrets

  • • Credentials encrypted opgeslagen
  • • Environment-specific configs
  • • Automatic rotation mogelijk
  • • Complete audit logging
  • • Zero-trust security
  • • GDPR/SOC2 compliant

🎯 Use Cases voor Variables

  • API Endpoints: Verschillende URLs voor dev/staging/prod
  • Feature Flags: Enable/disable functionaliteit per environment
  • Rate Limits: Verschillende limits per omgeving
  • Email Recipients: Test emails naar dev team, prod naar klanten
  • Database Connections: Separate databases per environment
  • Third-party Keys: Sandbox vs production API keys

Types Variables in N8N

N8N kent verschillende soorten variables, elk met hun eigen scope en use case:

N8N Variables Hierarchy System Environment OS Level: PATH, HOME, USER N8N Environment N8N Config: N8N_HOST, DB_TYPE, WEBHOOK_URL Workflow Variables $vars (Enterprise): Global workflow variables Node Variables $json, $input, $node: Node-specific data Scope: System-wide → Application → Workflow → Node-specific
Variable Type Scope Access Method Use Case
System Environment OS Level $env System paths, user info
N8N Environment Application .env file Database, webhooks, config
Workflow Variables All workflows $vars (Enterprise) Shared constants, API URLs
Credentials Node specific Credentials UI API keys, passwords
Node Variables Single node $json, $input Runtime data

Environment Variables Setup

Environment variables zijn de foundation voor N8N configuratie. Ze bepalen alles van database connections tot security settings:

📄 Complete .env Example

# ====================
# CORE CONFIGURATION
# ====================

# Basic Settings
N8N_HOST=localhost
N8N_PORT=5678
N8N_PROTOCOL=https
GENERIC_TIMEZONE=Europe/Amsterdam

# ====================
# DATABASE
# ====================

# PostgreSQL Configuration
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres.example.com
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n_production
DB_POSTGRESDB_USER=n8n_user
DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}  # Reference to secret
DB_POSTGRESDB_SSL=true
DB_POSTGRESDB_SSL_CA=/path/to/ca.pem

# ====================
# SECURITY
# ====================

# Encryption
N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}  # 32-byte hex string

# User Management  
N8N_USER_MANAGEMENT_DISABLED=false
N8N_USER_MANAGEMENT_EMAIL_VERIFICATION=true
N8N_USER_MANAGEMENT_JWT_SECRET=${JWT_SECRET}

# Security Headers
N8N_SECURITY_AUDIT_ENABLED=true
N8N_SECURITY_BLOCK_FILE_ACCESS=true

# ====================
# EXECUTION
# ====================

# Process Configuration
EXECUTIONS_PROCESS=main
EXECUTIONS_TIMEOUT=3600
EXECUTIONS_TIMEOUT_MAX=7200

# Data Management
EXECUTIONS_DATA_SAVE_ON_ERROR=all
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_SAVE_ON_PROGRESS=false
EXECUTIONS_DATA_MAX_AGE=336  # 14 days
EXECUTIONS_DATA_PRUNE=true

# ====================
# WEBHOOKS
# ====================

WEBHOOK_URL=https://n8n.example.com/
N8N_WEBHOOK_TUNNEL_URL=https://n8n.example.com/

# ====================
# EXTERNAL STORAGE
# ====================

# S3 for Binary Data
N8N_DEFAULT_BINARY_DATA_MODE=s3
N8N_BINARY_DATA_S3_BUCKET=n8n-binary-data
N8N_BINARY_DATA_S3_REGION=eu-west-1
AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY}
AWS_SECRET_ACCESS_KEY=${AWS_SECRET_KEY}

# ====================
# EXTERNAL SECRETS
# ====================

# HashiCorp Vault
N8N_EXTERNAL_SECRETS_ENABLED=true
N8N_EXTERNAL_SECRETS_PROVIDER=vault
VAULT_ADDR=https://vault.example.com:8200
VAULT_TOKEN=${VAULT_TOKEN}
VAULT_NAMESPACE=n8n

# AWS Secrets Manager (Alternative)
# N8N_EXTERNAL_SECRETS_PROVIDER=aws
# AWS_REGION=eu-west-1
# AWS_SECRETS_PREFIX=n8n/

# ====================
# MONITORING
# ====================

# Metrics
N8N_METRICS=true
N8N_METRICS_PREFIX=n8n_
N8N_METRICS_INCLUDE_DEFAULT=true
N8N_METRICS_INCLUDE_API_ENDPOINTS=true
N8N_METRICS_INCLUDE_API_PATH_LABEL=true

# Logging
N8N_LOG_LEVEL=info
N8N_LOG_OUTPUT=console
N8N_LOG_FILE_LOCATION=/var/log/n8n/

# ====================
# ENTERPRISE FEATURES
# ====================

N8N_LICENSE_KEY=${N8N_LICENSE}
N8N_WORKFLOW_VARIABLES_ENABLED=true
N8N_SSO_ENABLED=true
N8N_LDAP_ENABLED=true

⚠️ Security Tips voor Environment Variables

  • Never commit .env files: Add to .gitignore immediately
  • Use references: ${SECRET_VAR} verwijst naar system env
  • Rotate regularly: Change secrets elke 90 dagen
  • Minimum permissions: Restrictive file permissions (600)
  • Encrypt at rest: Use encrypted filesystems

Workflow Variables (Enterprise Feature)

Workflow variables ($vars) zijn een Enterprise feature die global variables mogelijk maakt across alle workflows:

🎯 Workflow Variables Setup

1. Enable in Configuration:

N8N_WORKFLOW_VARIABLES_ENABLED=true

2. Define Variables in UI:

Settings → Variables → Add Variable

{
  "API_BASE_URL": "https://api.example.com",
  "MAX_RETRY_COUNT": "3",
  "DEFAULT_TIMEOUT": "30",
  "ENVIRONMENT": "production",
  "FEATURE_FLAGS": {
    "enableNewFeature": true,
    "debugMode": false
  }
}

3. Use in Workflows:

// In Code node
const apiUrl = $vars.API_BASE_URL;
const maxRetries = parseInt($vars.MAX_RETRY_COUNT);

// In expressions
{{ $vars.ENVIRONMENT === 'production' ? 'prod-endpoint' : 'dev-endpoint' }}

// Access nested values
const debugEnabled = $vars.FEATURE_FLAGS.debugMode;

💡 Alternative voor Non-Enterprise: Static Data Node

Als je geen Enterprise hebt, kun je een workaround gebruiken met Static Data nodes:

// Create een "Config" workflow met Static Data node
{
  "apiUrl": process.env.API_URL || "https://api.example.com",
  "environment": process.env.NODE_ENV || "development",
  "features": {
    "newUI": process.env.FEATURE_NEW_UI === "true"
  }
}

// Reference in andere workflows via Execute Workflow node

.env Files & Configuration Management

Professionele .env file management voor verschillende environments:

📁 Multi-Environment File Structure

n8n-deployment/
├── environments/
│   ├── .env.development
│   ├── .env.staging
│   ├── .env.production
│   └── .env.example      # Template zonder secrets
├── secrets/
│   ├── vault-config.json
│   └── aws-secrets.json
├── docker-compose.yml
├── docker-compose.dev.yml
├── docker-compose.prod.yml
└── scripts/
    ├── load-env.sh
    ├── rotate-secrets.sh
    └── validate-config.sh

🔧 Dynamic Environment Loading Script

#!/bin/bash
# load-env.sh - Load environment-specific configuration

ENVIRONMENT=${1:-development}
ENV_FILE="./environments/.env.${ENVIRONMENT}"

if [ ! -f "$ENV_FILE" ]; then
    echo "Error: Environment file $ENV_FILE not found"
    exit 1
fi

# Load base configuration
set -a
source ./environments/.env.base

# Override with environment-specific
source "$ENV_FILE"

# Load secrets from external source
if [ "$ENVIRONMENT" = "production" ]; then
    # Fetch from HashiCorp Vault
    export DB_PASSWORD=$(vault kv get -field=password secret/n8n/db)
    export JWT_SECRET=$(vault kv get -field=jwt secret/n8n/auth)
    export ENCRYPTION_KEY=$(vault kv get -field=key secret/n8n/encryption)
else
    # Use local development secrets
    source ./environments/.env.secrets.local
fi

set +a

echo "✅ Loaded configuration for: $ENVIRONMENT"
echo "   N8N_HOST: $N8N_HOST"
echo "   DB_TYPE: $DB_TYPE"
echo "   ENVIRONMENT: $ENVIRONMENT"

# Start N8N with loaded config
exec n8n start

External Secrets Integration

N8N ondersteunt integratie met externe secret managers voor enterprise-grade security:

🔐 HashiCorp Vault

  • • Industry standard
  • • Dynamic secrets
  • • Audit logging
  • • Encryption as service

🔑 AWS Secrets Manager

  • • Native AWS integration
  • • Automatic rotation
  • • Cross-region replication
  • • IAM integration

🗝️ Azure Key Vault

  • • Azure native
  • • HSM support
  • • Managed identities
  • • Compliance certified

HashiCorp Vault Setup

Complete setup voor HashiCorp Vault integration met N8N:

🔐 Vault Configuration Steps

1. Install & Start Vault:

# Docker deployment
docker run -d \
  --name vault \
  --cap-add=IPC_LOCK \
  -e VAULT_DEV_ROOT_TOKEN_ID=myroot \
  -e VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 \
  -p 8200:8200 \
  hashicorp/vault:latest

# Initialize Vault (production)
vault operator init
vault operator unseal  # Repeat 3x with different keys

2. Configure Secrets Engine:

# Enable KV secrets engine
vault secrets enable -path=n8n kv-v2

# Write N8N secrets
vault kv put n8n/database \
  host="postgres.example.com" \
  port="5432" \
  username="n8n_user" \
  password="super-secret-password"

vault kv put n8n/encryption \
  key="$(openssl rand -hex 32)"

vault kv put n8n/jwt \
  secret="$(openssl rand -base64 48)"

3. Create N8N Policy:

# n8n-policy.hcl
path "n8n/*" {
  capabilities = ["read", "list"]
}

path "auth/token/renew-self" {
  capabilities = ["update"]
}

# Apply policy
vault policy write n8n-policy n8n-policy.hcl

# Create token with policy
vault token create -policy=n8n-policy -ttl=720h

4. N8N Integration:

// Custom Vault integration in Code node
const vault = require('node-vault')({
  endpoint: process.env.VAULT_ADDR,
  token: process.env.VAULT_TOKEN
});

// Read secret
const dbSecret = await vault.read('n8n/data/database');
const dbPassword = dbSecret.data.data.password;

// Use in connection
const connection = {
  host: dbSecret.data.data.host,
  port: dbSecret.data.data.port,
  user: dbSecret.data.data.username,
  password: dbPassword
};

AWS Secrets Manager Integration

AWS Secrets Manager biedt native cloud integration met automatic rotation:

☁️ AWS Secrets Manager Setup

1. Create Secrets in AWS:

# Create database credentials
aws secretsmanager create-secret \
  --name n8n/production/database \
  --secret-string '{
    "username": "n8n_user",
    "password": "generated-password",
    "host": "rds.amazonaws.com",
    "port": 5432
  }'

# Create API keys
aws secretsmanager create-secret \
  --name n8n/production/api-keys \
  --secret-string '{
    "stripe": "sk_live_...",
    "sendgrid": "SG...",
    "openai": "sk-..."
  }'

# Enable automatic rotation
aws secretsmanager rotate-secret \
  --secret-id n8n/production/database \
  --rotation-lambda-arn arn:aws:lambda:... \
  --rotation-rules AutomaticallyAfterDays=30

2. IAM Role for N8N:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "secretsmanager:GetSecretValue",
      "secretsmanager:DescribeSecret"
    ],
    "Resource": "arn:aws:secretsmanager:*:*:secret:n8n/*"
  }]
}

3. N8N Code Integration:

// AWS SDK integration in Code node
const AWS = require('aws-sdk');
const client = new AWS.SecretsManager({
  region: 'eu-west-1'
});

async function getSecret(secretName) {
  try {
    const data = await client.getSecretValue({
      SecretId: secretName
    }).promise();
    
    return JSON.parse(data.SecretString);
  } catch (error) {
    console.error('Failed to retrieve secret:', error);
    throw error;
  }
}

// Use in workflow
const dbCreds = await getSecret('n8n/production/database');
const apiKeys = await getSecret('n8n/production/api-keys');

// Connect to database
const pgConnection = {
  host: dbCreds.host,
  port: dbCreds.port,
  user: dbCreds.username,
  password: dbCreds.password
};

Multi-Environment Setup (Dev/Staging/Prod)

Professionele setup voor het beheren van meerdere environments:

🌍 Complete Multi-Environment Architecture

# docker-compose.override.yml pattern

# Base configuration (docker-compose.yml)
version: '3.8'
services:
  n8n:
    image: n8nio/n8n:latest
    env_file:
      - ./environments/.env.${ENVIRONMENT:-development}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - n8n_network

# Development override (docker-compose.dev.yml)
version: '3.8'
services:
  n8n:
    ports:
      - "5678:5678"
    environment:
      N8N_LOG_LEVEL: debug
      N8N_DIAGNOSTICS_ENABLED: true
    volumes:
      - ./workflows:/home/node/.n8n/workflows
      - ./custom:/home/node/.n8n/custom

# Staging override (docker-compose.staging.yml)
version: '3.8'
services:
  n8n:
    environment:
      N8N_HOST: staging.n8n.example.com
      N8N_WEBHOOK_URL: https://staging.n8n.example.com/
    deploy:
      replicas: 2

# Production override (docker-compose.prod.yml)
version: '3.8'
services:
  n8n:
    environment:
      N8N_HOST: n8n.example.com
      N8N_WEBHOOK_URL: https://n8n.example.com/
      EXECUTIONS_DATA_SAVE_ON_SUCCESS: none
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '4.0'
          memory: 8G

🚀 Deployment Script

#!/bin/bash
# deploy.sh - Smart deployment script

ENVIRONMENT=${1:-development}

case $ENVIRONMENT in
  development)
    docker-compose \
      -f docker-compose.yml \
      -f docker-compose.dev.yml \
      up -d
    ;;
  
  staging)
    # Load staging secrets
    export $(cat environments/.env.staging | xargs)
    
    docker-compose \
      -f docker-compose.yml \
      -f docker-compose.staging.yml \
      up -d --scale n8n=2
    ;;
  
  production)
    # Fetch production secrets from Vault
    ./scripts/fetch-prod-secrets.sh
    
    # Blue-green deployment
    docker-compose \
      -f docker-compose.yml \
      -f docker-compose.prod.yml \
      up -d --no-deps --scale n8n=3 n8n
    
    # Health check
    ./scripts/health-check.sh || rollback
    ;;
  
  *)
    echo "Unknown environment: $ENVIRONMENT"
    exit 1
    ;;
esac

echo "✅ Deployed to $ENVIRONMENT"

Security Best Practices

Implementeer deze best practices voor maximale security:

🔒 Security Checklist

Secrets Management:

  • ☑️ Never hardcode secrets
  • ☑️ Use external secret managers
  • ☑️ Rotate secrets regularly (90 days)
  • ☑️ Unique secrets per environment
  • ☑️ Encrypt secrets at rest
  • ☑️ Use least privilege principle

Access Control:

  • ☑️ Role-based access (RBAC)
  • ☑️ MFA for production access
  • ☑️ Audit logging enabled
  • ☑️ Regular access reviews
  • ☑️ Service accounts for automation
  • ☑️ Network segmentation

File Security:

  • ☑️ .env files in .gitignore
  • ☑️ File permissions 600
  • ☑️ Encrypted file systems
  • ☑️ Secure backup procedures
  • ☑️ Version control for configs

Monitoring:

  • ☑️ Secret access monitoring
  • ☑️ Anomaly detection
  • ☑️ Failed auth alerts
  • ☑️ Configuration drift detection
  • ☑️ Compliance scanning

⚠️ Common Security Mistakes

  • Committing .env files: Always check git status before push
  • Sharing secrets via Slack/Email: Use secure channels only
  • Using same secrets everywhere: Unique per environment
  • No expiration policy: Set TTL on all secrets
  • Broad permissions: Grant minimum required access
  • No backup keys: Always have recovery procedure

🚀 Start met Secure N8N Configuration

Implementeer enterprise-grade secrets management en multi-environment setup voor je N8N installatie. Veilig, schaalbaar en compliant!

Automation Scripts voor Secrets Management

Automatiseer je secrets management met deze utility scripts:

🔄 Automatic Secret Rotation Script

#!/usr/bin/env python3
# rotate-secrets.py - Automatic secret rotation

import os
import json
import boto3
import hvac
from datetime import datetime, timedelta
import secrets
import string

class SecretRotator:
    def __init__(self, provider='vault'):
        self.provider = provider
        if provider == 'vault':
            self.client = hvac.Client(
                url=os.environ['VAULT_ADDR'],
                token=os.environ['VAULT_TOKEN']
            )
        elif provider == 'aws':
            self.client = boto3.client('secretsmanager')
    
    def generate_password(self, length=32):
        """Generate secure random password"""
        alphabet = string.ascii_letters + string.digits + '!@#$%^&*'
        return ''.join(secrets.choice(alphabet) for _ in range(length))
    
    def rotate_database_password(self):
        """Rotate database password"""
        new_password = self.generate_password()
        
        # Update in secret manager
        if self.provider == 'vault':
            self.client.secrets.kv.v2.create_or_update_secret(
                path='database',
                mount_point='n8n',
                secret={'password': new_password}
            )
        
        # Update database user
        # postgres.execute(f"ALTER USER n8n PASSWORD '{new_password}'")
        
        # Log rotation
        print(f"✅ Database password rotated at {datetime.now()}")
        
    def rotate_api_keys(self):
        """Rotate API keys for external services"""
        services = ['stripe', 'sendgrid', 'twilio']
        
        for service in services:
            # Call service API to generate new key
            # new_key = service_api.regenerate_key()
            
            # Store in secret manager
            if self.provider == 'vault':
                self.client.secrets.kv.v2.create_or_update_secret(
                    path=f'api-keys/{service}',
                    mount_point='n8n',
                    secret={'key': 'new_key_here'}
                )
            
            print(f"✅ {service} API key rotated")
    
    def check_expiration(self):
        """Check which secrets need rotation"""
        secrets_to_rotate = []
        
        # Check all secrets
        list_response = self.client.secrets.kv.v2.list_secrets(
            mount_point='n8n'
        )
        
        for secret in list_response['data']['keys']:
            metadata = self.client.secrets.kv.v2.read_secret_metadata(
                path=secret,
                mount_point='n8n'
            )
            
            created_time = metadata['data']['created_time']
            age_days = (datetime.now() - created_time).days
            
            if age_days > 90:
                secrets_to_rotate.append(secret)
        
        return secrets_to_rotate

if __name__ == '__main__':
    rotator = SecretRotator()
    
    # Check and rotate expired secrets
    expired = rotator.check_expiration()
    for secret in expired:
        print(f"⚠️ Secret '{secret}' needs rotation")
    
    # Perform rotation
    rotator.rotate_database_password()
    rotator.rotate_api_keys()

Conclusie

Proper variables en secrets management is cruciaal voor enterprise-ready N8N deployments. Door environment variables, workflow variables en externe secret managers te combineren, creëer je een veilige, schaalbare en maintainable automation platform.

Start met een basis .env setup, implementeer geleidelijk external secrets, en werk toe naar volledig automated secret rotation. Je workflows worden veiliger, je compliance verbetert, en je team kan met vertrouwen automatiseren.

Hulp nodig met secure N8N configuration? Contact me voor professional setup en security audit!

#n8n #environment variables #secrets management #workflow variables #security #configuration #vault #credentials