All posts
Nodox Team··12 min read

n8n Self-Hosting Guide: Docker Setup, Security, and Production Best Practices

Complete guide to self-hosting n8n with Docker. Learn deployment, security hardening, backups, monitoring, and scaling for production use.

n8nself-hostingdockerdevopstutorial

n8n Cloud is convenient. But self-hosting gives you control, privacy, and potentially significant cost savings.

Running n8n yourself isn't complicated — but doing it well requires knowing what matters.

Here's the complete guide to self-hosting n8n for production use.

Why Self-Host?

Reasons to self-host:

  • Data privacy — Your data stays on your infrastructure
  • Cost control — Predictable costs, especially at scale
  • Customization — Modify n8n, add custom nodes
  • No execution limits — Run as many workflows as your hardware allows
  • Compliance — Meet regulatory requirements for data handling

Reasons to use Cloud:

  • No infrastructure management
  • Automatic updates and backups
  • Built-in scaling
  • Faster setup

For most individuals and small teams, Cloud makes sense. For organizations with specific requirements, self-hosting is worth the effort.

Basic Docker Setup

The fastest way to get n8n running:

```bash

docker run -it --rm \

--name n8n \

-p 5678:5678 \

-v n8n_data:/home/node/.n8n \

docker.n8n.io/n8nio/n8n

```

That's it. Open localhost:5678.

But this isn't production-ready. Let's fix that.

Production Docker Compose Setup

Create a docker-compose.yml:

```yaml

version: '3.8'

services:

n8n:

image: docker.n8n.io/n8nio/n8n

restart: always

ports:

- "5678:5678"

environment:

- N8N_HOST=your-domain.com

- N8N_PORT=5678

- N8N_PROTOCOL=https

- WEBHOOK_URL=https://your-domain.com/

- GENERIC_TIMEZONE=America/New_York

- N8N_SECURE_COOKIE=true

- N8N_ENCRYPTION_KEY=your-random-encryption-key

- DB_TYPE=postgresdb

- DB_POSTGRESDB_HOST=postgres

- DB_POSTGRESDB_PORT=5432

- DB_POSTGRESDB_DATABASE=n8n

- DB_POSTGRESDB_USER=n8n

- DB_POSTGRESDB_PASSWORD=your-db-password

volumes:

- n8n_data:/home/node/.n8n

depends_on:

- postgres

postgres:

image: postgres:15

restart: always

environment:

- POSTGRES_USER=n8n

- POSTGRES_PASSWORD=your-db-password

- POSTGRES_DB=n8n

volumes:

- postgres_data:/var/lib/postgresql/data

volumes:

n8n_data:

postgres_data:

```

Why PostgreSQL?

By default, n8n uses SQLite. This works but has limitations:

  • Doesn't handle concurrent writes well
  • Harder to back up while running
  • Not suitable for high-volume production

PostgreSQL is the recommended production database.

Key Environment Variables

```

N8N_ENCRYPTION_KEY

```

Critical. Encrypts your credentials. Generate a random 32+ character string and keep it safe. If you lose this, you lose access to all stored credentials.

```

WEBHOOK_URL

```

Your public URL for webhooks. Must match your actual domain.

```

N8N_SECURE_COOKIE=true

```

Requires HTTPS. Enable this in production.

Reverse Proxy with Nginx

Don't expose n8n directly. Use a reverse proxy.

```nginx

server {

listen 80;

server_name your-domain.com;

return 301 https://\$server_name\$request_uri;

}

server {

listen 443 ssl http2;

server_name your-domain.com;

ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;

ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

location / {

proxy_pass http://localhost:5678;

proxy_http_version 1.1;

proxy_set_header Upgrade \$http_upgrade;

proxy_set_header Connection "upgrade";

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;

# Important for webhooks

proxy_buffering off;

proxy_cache off;

chunked_transfer_encoding on;

}

}

```

Don't forget: The WebSocket upgrade headers are essential for n8n's UI to work properly.

Security Hardening

1. Enable Authentication

n8n 1.0+ includes built-in user management. On first launch, you'll create an owner account with email and password. This is enabled by default.

For additional security, you can:

  • Require email verification for new users
  • Set up SSO/SAML for enterprise (paid feature)
  • Use environment variables to pre-configure the owner account:

```

N8N_USER_MANAGEMENT_JWT_SECRET=your-random-secret-string

```

Note: The old basic auth variables (N8N_BASIC_AUTH_*) were deprecated in n8n 1.0.

2. Disable Public API (If Not Needed)

```

N8N_PUBLIC_API_DISABLED=true

```

3. Restrict Webhook Access

Consider IP whitelisting for webhook endpoints if you know which services will call them.

4. Secure the Database

  • Use strong passwords
  • Don't expose PostgreSQL port publicly
  • Enable SSL for database connections if over network

5. Keep Updated

Subscribe to n8n's security announcements. Update regularly.

```bash

docker-compose pull

docker-compose up -d

```

6. Network Isolation

Run n8n in its own network:

```yaml

networks:

n8n_network:

driver: bridge

```

Only expose what's necessary.

Backup Strategy

Database Backups

For PostgreSQL:

```bash

docker exec n8n-postgres pg_dump -U n8n n8n > backup-$(date +%Y%m%d).sql

```

Schedule this daily via cron.

Volume Backups

The n8n data volume contains:

  • Workflow files (if using filesystem)
  • Custom node configurations
  • Local settings

Back up the Docker volume:

```bash

docker run --rm \

-v n8n_data:/source:ro \

-v /backups:/backup \

alpine tar czf /backup/n8n-data-$(date +%Y%m%d).tar.gz -C /source .

```

Backup the Encryption Key

If you lose N8N_ENCRYPTION_KEY, you lose all credentials. Store it securely:

  • Password manager
  • Encrypted secrets file
  • Hardware security module (for enterprises)

Test Your Backups

A backup you haven't tested is not a backup. Periodically restore to a test environment.

Monitoring

Basic Health Check

Add to docker-compose:

```yaml

healthcheck:

test: ["CMD", "wget", "-q", "--spider", "http://localhost:5678/healthz"]

interval: 30s

timeout: 10s

retries: 3

```

Execution Monitoring

n8n stores execution history. Query it:

  • Set up alerts for failed executions
  • Monitor execution duration trends
  • Track active workflow count

External Monitoring

Use tools like:

  • Uptime Kuma (self-hosted)
  • Better Uptime
  • Datadog / New Relic

Monitor both the n8n endpoint and critical webhook URLs.

Log Management

Docker logs fill up fast. Configure log rotation:

```yaml

logging:

driver: "json-file"

options:

max-size: "10m"

max-file: "3"

```

Or ship logs to a centralized system (Loki, ELK, etc.).

Scaling Considerations

Vertical Scaling

n8n is single-threaded per workflow execution. For more throughput:

  • More CPU cores help with concurrent workflows
  • More RAM for large data transformations
  • Fast SSD for database performance

Horizontal Scaling (Queue Mode)

For high-volume production, n8n supports queue mode with Redis:

```

EXECUTIONS_MODE=queue

QUEUE_BULL_REDIS_HOST=redis

QUEUE_BULL_REDIS_PORT=6379

```

This separates:

  • Main process — Handles UI, webhook reception
  • Worker processes — Execute workflows

You can run multiple workers for parallel execution.

When to Scale

Signs you need more resources:

  • Webhook responses timing out
  • Execution queue backing up
  • UI becoming slow
  • Database CPU maxed out

Upgrading n8n

Minor Updates

Usually safe. Pull and restart:

```bash

docker-compose pull

docker-compose up -d

```

Major Updates

Check the changelog for breaking changes. Test first:

  1. Back up everything
  2. Create test environment
  3. Test critical workflows
  4. Update production

Rollback Plan

Keep the previous image available:

```bash

docker tag docker.n8n.io/n8nio/n8n:latest docker.n8n.io/n8nio/n8n:backup

```

If something breaks, revert:

```bash

docker-compose down

# Update docker-compose.yml to use :backup tag

docker-compose up -d

```

Common Self-Hosting Mistakes

Mistake 1: No Encryption Key Backup

Lost encryption key = lost credentials = rebuild every integration.

Fix: Back up the key immediately. Store it separately from the database backup.

Mistake 2: SQLite in Production

Works until it doesn't. You'll hit issues at scale.

Fix: Use PostgreSQL from the start. Migration is painful later.

Mistake 3: No HTTPS

Credentials sent over HTTP are visible to anyone watching.

Fix: Always use HTTPS. Let's Encrypt is free.

Mistake 4: Exposed Database

PostgreSQL port open to the internet is an invitation for attacks.

Fix: Keep database internal to Docker network.

Mistake 5: No Monitoring

You don't know workflows are failing until users complain.

Fix: Set up Error Trigger workflow + external uptime monitoring.

Self-Hosting Checklist

Before going to production:

  • [ ] PostgreSQL database (not SQLite)
  • [ ] HTTPS enabled
  • [ ] Authentication configured
  • [ ] Encryption key backed up securely
  • [ ] Reverse proxy configured
  • [ ] Automated backups scheduled
  • [ ] Backup restore tested
  • [ ] Monitoring in place
  • [ ] Error notification workflow active
  • [ ] Resource limits set (memory, CPU)
  • [ ] Log rotation configured
  • [ ] Update process documented

Ready to master n8n deployments? Nodox.ai challenges include infrastructure scenarios where you build production-ready automations. Learn by doing, not just reading documentation.

Start building today

Stop reading. Start building.

The best way to learn automation is by doing. Nodox.ai gives you hands-on challenges that build real skills — no passive tutorials, no hand-holding. Just problems to solve and skills that compound.