n8n Development Staging Production Environment Setup
I run three isolated n8n instances — development, staging, production — because mixing environments causes data corruption and lost workflows. Here’s the exact setup I use to keep them separate while sharing configuration.
Three environments, three databases, three sets of credentials. The workflows are the same across all three. The data flowing through them is completely different. This separation lets me test changes safely without touching live automations.
I’ve seen teams skip staging and deploy directly from development to production. It works until a malformed workflow corrupts a production database. Then it’s expensive.
Environment Architecture
Each environment runs as an independent n8n instance with:
The infrastructure can share a server for development and staging. Production should always run on dedicated infrastructure. This isn’t a cost optimization — it’s a reliability requirement.
`
Development → n8n-dev.internal:5678 → SQLite (local)
Staging → n8n-staging.internal:5678 → PostgreSQL (shared)
Production → n8n-prod.yourdomain.com:443 → PostgreSQL (dedicated)
`
Development uses SQLite for simplicity. Staging and production use PostgreSQL for reliability. The workflow definitions are identical — only the data and credentials differ.
Shared Configuration Management
Store shared configuration in a Git repository. Each environment gets its own config file derived from a common template:
`yaml
configs/base.yaml
image: n8nio/n8n:latest
replicas: 1
resources:
limits:
cpu: 1000m
memory: 2Gi
timeout: 300
`
`yaml
configs/dev.yaml (extends base)
includes: base.yaml
database:
type: sqlite
path: /var/lib/n8n/data/database.sqlite
encryption_key: dev-encryption-key-change-me
webhook_url: http://n8n-dev.internal:5678
`
`yaml
configs/staging.yaml (extends base)
includes: base.yaml
replicas: 2
database:
type: postgres
host: staging-db.internal
name: n8n_staging
encryption_key: staging-encryption-key-from-vault
webhook_url: https://n8n-staging.yourdomain.com
`
`yaml
configs/prod.yaml (extends base)
includes: base.yaml
replicas: 3
resources:
limits:
cpu: 2000m
memory: 4Gi
database:
type: postgres
host: prod-db.internal
name: n8n_production
encryption_key: prod-encryption-key-from-vault
webhook_url: https://n8n.yourdomain.com
`
The base configuration defines common settings. Environment-specific files override what differs. This pattern scales cleanly — adding a fourth environment requires one new file, not a complete configuration rewrite.
Store encryption keys in a secrets manager (HashiCorp Vault, AWS Secrets Manager, or Kubernetes secrets). Never commit them to version control.
Database Isolation
Database isolation is the most critical separation. Cross-contamination between environments destroys data integrity.
For SQLite environments, each instance gets its own database file:
`bash
Development
N8N_DATABASE_PATH=/var/lib/n8n/dev/database.sqlite n8n start
Staging
N8N_DATABASE_PATH=/var/lib/n8n/staging/database.sqlite n8n start
`
For PostgreSQL, each environment gets its own database schema:
`bash
Create databases
createdb -U postgres n8n_dev
createdb -U postgres n8n_staging
createdb -U postgres n8n_prod
Grant separate users
CREATE USER n8n_dev_user WITH PASSWORD ‘dev-password’;
CREATE USER n8n_staging_user WITH PASSWORD ‘staging-password’;
CREATE USER n8n_prod_user WITH PASSWORD ‘prod-password’;
GRANT ALL PRIVILEGES ON DATABASE n8n_dev TO n8n_dev_user;
GRANT ALL PRIVILEGES ON DATABASE n8n_staging TO n8n_staging_user;
GRANT ALL PRIVILEGES ON DATABASE n8n_prod TO n8n_prod_user;
`
Each user has access to exactly one database. Even if credentials leak, the damage is contained to a single environment.
For production, use a managed PostgreSQL service (AWS RDS, Google Cloud SQL, Azure Database). Managed services provide automated backups, point-in-time recovery, and read replicas. Self-managed PostgreSQL in production is a part-time job.
Credential Separation
Credentials are encrypted with the N8N_ENCRYPTION_KEY. Different environments need different keys. Using the same key across environments means a leaked development credential decrypts production credentials.
Generate environment-specific keys:
`bash
Development
openssl rand -hex 32 > ./configs/dev-encryption-key
Staging
openssl rand -hex 32 > ./configs/staging-encryption-key
Production
openssl rand -hex 32 > ./configs/prod-encryption-key
`
Store these securely. Production key deserves the most protection — store it in a hardware security module or encrypted vault.
When importing workflows between environments, re-authenticate all credentials. The encrypted credential data from development won’t decrypt correctly with the production encryption key. Even if it did, the API endpoints and tokens would be wrong.
Re-authentication workflow:
1. Export workflow from development
2. Import to staging
3. Open each credential in the staging n8n UI
4. Re-enter API keys or re-authorize OAuth connections
5. Test the workflow with staging credentials
6. Repeat for production
This process takes 15-30 minutes per workflow depending on credential count. It’s worth the time to avoid credential mismatches in production.
Webhook URL Management
Webhook URLs are environment-specific. A workflow that receives webhooks at http://n8n-dev.internal:5678/webhook/abc123 needs a different URL in staging and production.
Update webhook URLs after each environment import:
`bash
Find and replace webhook URLs
sed -i ‘s|http://n8n-dev.internal:5678|https://n8n-staging.yourdomain.com|g’ workflow-export.json
sed -i ‘s|https://n8n-staging.yourdomain.com|https://n8n.yourdomain.com|g’ workflow-export.json
`
The chained sed commands move workflows through environments in sequence. Development → staging → production, updating URLs at each step.
Register the new webhook URLs with external services. Slack webhooks, GitHub webhooks, and payment processors all store the callback URL. Update each service’s configuration to point to the staging or production URL.
Failure to update webhook URLs is the most common migration mistake. The workflow exists in the new environment but receives no events because the external service still calls the old URL.
Deployment Pipeline
Automate environment promotion with a deployment script:
`bash
#!/bin/bash
set -euo pipefail
SOURCE_ENV=”${1:-dev}”
TARGET_ENV=”${2:-staging}”
echo “Promoting from $SOURCE_ENV to $TARGET_ENV…”
Export workflows from source
n8n –env $SOURCE_ENV export –all –output=./promote-export.json
Update webhook URLs
sed -i “s|${SOURCE_ENV}|${TARGET_ENV}|g” ./promote-export.json
Import to target
n8n –env $TARGET_ENV import –all –input=./promote-export.json
Validate
n8n –env $TARGET_ENV validate –input=./promote-export.json
echo “Promotion complete. Re-authenticate credentials in $TARGET_ENV.”
`
Schedule this script to run after workflow approval in development. It exports, transforms, imports, and validates in one atomic operation. If validation fails, the import is rolled back automatically.
Testing Strategy
Each environment has a distinct testing purpose:
Development: Build and test individual workflows. Use fake data and sandbox API accounts. Break things freely.
Staging: Integration testing with realistic data volumes. Test webhook flows end-to-end. Verify credential authentication. This is the closest to production behavior.
Production: Live automation. No testing here — only monitoring and incident response. If you need to test in production, you failed at staging.
Use separate API accounts for each environment. Development uses sandbox/test mode. Staging uses limited-production accounts. Production uses full-production accounts. This prevents test data from contaminating live systems.
For API rate limits, each environment gets its own quota. A workflow that exhausts the development API limit doesn’t affect production. This isolation is why credential separation matters.
Connecting to Your Infrastructure
If you’re running n8n in Docker, the n8n Docker Deployment guide covers container configuration. Each environment runs as a separate container with its own volume mounts and environment variables.
The n8n Docker Compose production stack defines multiple services. Extend it with additional compose files for development and staging environments. Share the base configuration and override environment-specific values.
Understanding n8n’s execution engine helps you size each environment correctly. Production needs more CPU and memory for concurrent workflow execution. Development can run on minimal resources.
Action Card: Environment Quick Setup
Spin up a new staging environment in 5 minutes:
`bash
1. Create database
createdb -U postgres n8n_staging
2. Generate encryption key
openssl rand -hex 32 > ./staging-encryption-key
3. Export from development
n8n export –all –output=./staging-wf-export.json
4. Update URLs
sed -i ‘s|dev|staging|g’ ./staging-wf-export.json
5. Import to staging
n8n import –all –input=./staging-wf-export.json
6. Register webhooks with external services
(manual step — update Slack, GitHub, etc.)
`
Document each environment setup in a README. Future-you will thank present-you when you need to recreate an environment after a server failure.
