| layout | default |
|---|---|
| title | n8n AI Tutorial - Chapter 8: Production Deployment |
| nav_order | 8 |
| has_children | false |
| parent | n8n AI Tutorial |
Welcome to Chapter 8: Production Deployment and Scaling. In this part of n8n AI Tutorial: Workflow Automation with AI, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
Deploy n8n AI workflows to production with monitoring, security, and enterprise features.
# docker-compose.prod.yml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
ports:
- "5678:5678"
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_BASIC_AUTH_ACTIVE=false
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n_prod
- DB_POSTGRESDB_USER=n8n_user
- DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
- EXECUTIONS_PROCESS=main
- QUEUE_BULL_REDIS_HOST=redis
- QUEUE_BULL_REDIS_PORT=6379
- N8N_LOG_LEVEL=info
- N8N_METRICS=true
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
- redis
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5678/healthz"]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_DB=n8n_prod
- POSTGRES_USER=n8n_user
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n_user -d n8n_prod"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
volumes:
n8n_data:
postgres_data:
redis_data:-- Production database optimizations
ALTER SYSTEM SET shared_buffers = '256MB';
ALTER SYSTEM SET effective_cache_size = '1GB';
ALTER SYSTEM SET maintenance_work_mem = '64MB';
ALTER SYSTEM SET checkpoint_completion_target = 0.9;
ALTER SYSTEM SET wal_buffers = '16MB';
ALTER SYSTEM SET default_statistics_target = 100;
-- Create indexes for n8n tables
CREATE INDEX CONCURRENTLY idx_execution_workflow_id ON execution_entity(workflowId);
CREATE INDEX CONCURRENTLY idx_execution_started_at ON execution_entity(startedAt);
CREATE INDEX CONCURRENTLY idx_workflow_active ON workflow_entity(active);
-- Enable pg_stat_statements for monitoring
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
labels:
app: n8n
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: n8n
template:
metadata:
labels:
app: n8n
spec:
containers:
- name: n8n
image: n8nio/n8n:latest
ports:
- containerPort: 5678
env:
- name: N8N_ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: n8n-secrets
key: encryption-key
- name: DB_POSTGRESDB_HOST
value: "postgres-service"
- name: QUEUE_BULL_REDIS_HOST
value: "redis-service"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /healthz
port: 5678
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /healthz
port: 5678
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3# nginx.conf
upstream n8n_backend {
least_conn;
server n8n-1:5678;
server n8n-2:5678;
server n8n-3:5678;
}
server {
listen 80;
server_name workflows.yourcompany.com;
# Rate limiting
limit_req zone=api burst=10 nodelay;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
location / {
proxy_pass http://n8n_backend;
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;
# Timeout settings
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# WebSocket support for real-time updates
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Static file caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}# Environment variables for security
environment:
- N8N_BASIC_AUTH_ACTIVE=false
- N8N_JWT_SECRET=${JWT_SECRET}
- N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
# OAuth configuration
- N8N_OAUTH_GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- N8N_OAUTH_GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
# SAML/SSO (Enterprise)
- N8N_SAML_ENABLED=true
- N8N_SAML_METADATA_URL=${SAML_METADATA_URL}# Kubernetes network policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: n8n-network-policy
spec:
podSelector:
matchLabels:
app: n8n
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 5678
egress:
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
- to: [] # Allow external API calls
ports:
- protocol: TCP
port: 443# prometheus.yml
scrape_configs:
- job_name: 'n8n'
static_configs:
- targets: ['n8n-service:5678']
metrics_path: '/metrics'
scrape_interval: 15s// Custom workflow monitoring
const workflowStats = $workflow.expression.get('workflow_stats') || {
executions: 0,
success_count: 0,
failure_count: 0,
avg_execution_time: 0
};
workflowStats.executions += 1;
if ($input.item.json.success) {
workflowStats.success_count += 1;
} else {
workflowStats.failure_count += 1;
}
const executionTime = $input.item.json.execution_time || 0;
workflowStats.total_execution_time = (workflowStats.total_execution_time || 0) + executionTime;
workflowStats.avg_execution_time = workflowStats.total_execution_time / workflowStats.executions;
$workflow.expression.set('workflow_stats', workflowStats);
// Send metrics to external system
const metrics = {
workflow_id: $workflow.id,
stats: workflowStats,
timestamp: new Date().toISOString()
};
// Could send to Datadog, New Relic, etc.
console.log('Workflow metrics:', JSON.stringify(metrics));# k8s/backup-cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: n8n-backup
spec:
schedule: "0 2 * * *" # Daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: postgres:15-alpine
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: n8n-secrets
key: db-password
command:
- /bin/sh
- -c
- |
# Database backup
pg_dump -h postgres-service -U n8n_user n8n_prod > /backup/n8n_$(date +%Y%m%d_%H%M%S).sql
# Compress
gzip /backup/n8n_*.sql
# Upload to S3 (if using external backup)
# aws s3 cp /backup/ s3://n8n-backups/ --recursive --exclude "*" --include "*.gz"
restartPolicy: OnFailure
volumes:
- name: backup-volume
emptyDir: {}// Optimize AI calls with caching
const cache = $workflow.expression.get('ai_cache') || {};
const prompt = $input.item.json.prompt;
const cacheKey = require('crypto').createHash('md5').update(prompt).digest('hex');
if (cache[cacheKey] && (Date.now() - cache[cacheKey].timestamp) < 3600000) {
// Return cached result
return [cache[cacheKey].result];
}
// Make AI call
const aiResponse = await $node.openAi.default.sendMessage({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: prompt }]
});
// Cache result
cache[cacheKey] = {
result: aiResponse,
timestamp: Date.now()
};
$workflow.expression.set('ai_cache', cache);
return [aiResponse];# Redis-based queuing
environment:
- QUEUE_BULL_REDIS_HOST=redis-service
- QUEUE_BULL_REDIS_PORT=6379
- EXECUTIONS_PROCESS=main
- EXECUTIONS_TIMEOUT=3600
- EXECUTIONS_TIMEOUT_MAX=7200// Track AI API costs
const costTracker = $workflow.expression.get('cost_tracker') || {
total_cost: 0,
requests_by_model: {},
monthly_cost: {}
};
const model = $input.item.json.model || 'unknown';
const usage = $input.item.json.usage || {};
const cost = calculateCost(model, usage);
// Update totals
costTracker.total_cost += cost;
costTracker.requests_by_model[model] = (costTracker.requests_by_model[model] || 0) + 1;
// Monthly tracking
const month = new Date().toISOString().slice(0, 7);
costTracker.monthly_cost[month] = (costTracker.monthly_cost[month] || 0) + cost;
$workflow.expression.set('cost_tracker', costTracker);
function calculateCost(model, usage) {
const rates = {
'gpt-4': { prompt: 0.03, completion: 0.06 },
'gpt-4-turbo': { prompt: 0.01, completion: 0.03 },
'gpt-3.5-turbo': { prompt: 0.0015, completion: 0.002 },
'claude-3-opus-20240229': { prompt: 0.015, completion: 0.075 }
};
const modelRates = rates[model] || { prompt: 0, completion: 0 };
const promptTokens = usage.prompt_tokens || 0;
const completionTokens = usage.completion_tokens || 0;
return (promptTokens * modelRates.prompt + completionTokens * modelRates.completion) / 1000;
}# Multi-region deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n-us-east
labels:
app: n8n
region: us-east
spec:
replicas: 2
# Similar spec as main deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n-us-west
labels:
app: n8n
region: us-west
spec:
replicas: 2
# Similar spec as main deployment// Comprehensive audit logging
const auditLog = {
timestamp: new Date().toISOString(),
user_id: $input.item.json.user_id || 'system',
workflow_id: $workflow.id,
node_name: $node.name,
action: $input.item.json.action || 'execution',
input_data: $input.item.json,
ip_address: $input.item.json.ip || 'unknown',
user_agent: $input.item.json.user_agent || 'unknown'
};
// Store in database or send to SIEM
console.log('AUDIT:', JSON.stringify(auditLog));- Resource Planning: Calculate required resources based on workflow complexity
- Monitoring: Implement comprehensive monitoring and alerting
- Security: Use enterprise authentication and network security
- Backup: Regular automated backups with testing
- Scaling: Plan for horizontal scaling and load balancing
- Cost Control: Monitor and optimize AI API usage
- Compliance: Implement audit logging and data governance
- Testing: Thorough testing before production deployment
Production deployment transforms n8n AI workflows into enterprise-grade automation systems. With proper monitoring, security, and scaling, these workflows can handle millions of executions while maintaining reliability and performance.
Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for name, workflowStats, spec so behavior stays predictable as complexity grows.
In practical terms, this chapter helps you avoid three common failures:
- coupling core logic too tightly to one implementation path
- missing the handoff boundaries between setup, execution, and validation
- shipping changes without clear rollback or observability strategy
After working through this chapter, you should be able to reason about Chapter 8: Production Deployment and Scaling as an operating subsystem inside n8n AI Tutorial: Workflow Automation with AI, with explicit contracts for inputs, state transitions, and outputs.
Use the implementation notes around input, item, json as your checklist when adapting these patterns to your own repository.
Under the hood, Chapter 8: Production Deployment and Scaling usually follows a repeatable control path:
- Context bootstrap: initialize runtime config and prerequisites for
name. - Input normalization: shape incoming data so
workflowStatsreceives stable contracts. - Core execution: run the main logic branch and propagate intermediate state through
spec. - Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
- Output composition: return canonical result payloads for downstream consumers.
- Operational telemetry: emit logs/metrics needed for debugging and performance tuning.
When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.
Use the following upstream sources to verify implementation details while reading this chapter:
- View Repo
Why it matters: authoritative reference on
View Repo(github.com). - Awesome Code Docs
Why it matters: authoritative reference on
Awesome Code Docs(github.com).
Suggested trace strategy:
- search upstream code for
nameandworkflowStatsto map concrete implementation paths - compare docs claims against actual runtime/config code before reusing patterns in production