n8n Docker Setup: The Complete Guide to Self-Hosting n8n with Docker
n8n Docker Setup: The Complete Guide to Self-Hosting n8n with Docker
• Logic Workflow Team

n8n Docker Setup: The Complete Guide to Self-Hosting n8n with Docker

#n8n #Docker #self-hosting #DevOps #tutorial #Docker Compose

Docker is the fastest path to a production-ready n8n deployment. Skip the dependency nightmares, environment conflicts, and manual configuration headaches. With a single command, you get a fully isolated n8n instance running on any server.

Most tutorials stop at the basic docker run command. That works for testing but falls apart in production. This guide covers everything: from your first container to a hardened, scalable deployment with PostgreSQL, automatic SSL, and proper monitoring.

The Docker Advantage

Running n8n in Docker provides isolation that native installations cannot match. Your automation platform stays separate from the host system. Dependencies never conflict. Updates become trivial. Rollbacks take seconds instead of hours.

Docker also makes your deployment portable. The same configuration runs identically on your laptop, a $5 VPS, or enterprise Kubernetes. No more “works on my machine” problems.

For organizations weighing self-hosted versus n8n Cloud, Docker dramatically reduces the operational complexity argument. A properly containerized n8n instance requires minimal ongoing maintenance while giving you complete data control and significant cost savings at scale.

What You’ll Learn

  • Quick Start: Run n8n in Docker with one command
  • Production Setup: Docker Compose with PostgreSQL and health checks
  • Environment Variables: Complete reference for configuration
  • SSL/HTTPS: Automatic certificates with Traefik or Nginx
  • Troubleshooting: Fix common Docker-specific issues
  • Upgrades: Update n8n without losing data
  • Security: Harden your deployment against attacks

Prerequisites

Before deploying n8n, ensure your system meets these requirements.

Docker and Docker Compose

You need Docker Engine version 20.10 or later. Docker Compose comes bundled with modern Docker installations.

Verify your installation:

docker --version
docker compose version

Both commands should return version numbers. If docker compose fails, you may have the older standalone version. Install the plugin or use docker-compose (with hyphen) instead.

Hardware Recommendations

WorkloadCPURAMStorage
Testing/Development1 core1 GB10 GB
Light Production2 cores2 GB20 GB
Standard Production2 cores4 GB50 GB
Heavy Workflows4+ cores8+ GB100+ GB

For workflows processing large files, complex JSON, or AI operations, lean toward higher specs. Running out of memory crashes your instance and corrupts execution data.

Quick Start: Basic Docker Setup

Get n8n running in under two minutes. This setup uses SQLite and stores data in a Docker volume.

Create a Volume and Run n8n

docker volume create n8n_data

docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="America/New_York" \
  -e TZ="America/New_York" \
  -v n8n_data:/home/node/.n8n \
  --restart unless-stopped \
  docker.n8n.io/n8nio/n8n

Replace America/New_York with your timezone. The --restart unless-stopped flag ensures n8n starts automatically after reboots.

Access the Web Interface

Open http://localhost:5678 in your browser. You’ll see the n8n setup wizard prompting you to create an owner account.

Note: This basic setup works for testing and personal use. For anything beyond experimentation, use the Docker Compose setup below with PostgreSQL.

Understanding the Volume Mount

The -v n8n_data:/home/node/.n8n flag is critical. Without it, your workflows, credentials, and execution history disappear when the container restarts.

The volume stores:

  • Workflow definitions
  • Encrypted credentials
  • Execution logs
  • Encryption keys
  • Configuration settings

Never skip the volume mount. We’ve seen users lose months of work from this single oversight. Our self-hosting mistakes guide covers more pitfalls like this.


Production Setup with Docker Compose

For production deployments, Docker Compose orchestrates multiple services: n8n, PostgreSQL for the database, and optionally a reverse proxy for SSL.

Project Structure

Create a dedicated directory for your n8n deployment:

mkdir n8n-docker && cd n8n-docker

You’ll create two files: docker-compose.yml and .env.

Docker Compose Configuration

Create docker-compose.yml with this production-ready configuration:

services:
  postgres:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    ports:
      - "5678:5678"
    environment:
      # Database Configuration
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}

      # Security
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}

      # Instance Settings
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=${N8N_PROTOCOL}
      - WEBHOOK_URL=${WEBHOOK_URL}

      # Timezone
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${GENERIC_TIMEZONE}

      # Performance
      - N8N_RUNNERS_ENABLED=true
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=168
      - EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
    volumes:
      - n8n_data:/home/node/.n8n
      - ./local-files:/files
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

Environment File

Create .env in the same directory:

# PostgreSQL
POSTGRES_USER=n8n
POSTGRES_PASSWORD=your-secure-database-password
POSTGRES_DB=n8n

# n8n Configuration
N8N_ENCRYPTION_KEY=your-32-character-encryption-key-here
N8N_HOST=localhost
N8N_PROTOCOL=http
WEBHOOK_URL=http://localhost:5678/
GENERIC_TIMEZONE=America/New_York

Generate a secure encryption key:

openssl rand -hex 32

Copy the output to N8N_ENCRYPTION_KEY. This key encrypts credentials in the database. Lose it, and you lose access to all stored credentials.

Critical: Back up your encryption key separately from your database backups. Store it in a password manager or secrets vault.

Start the Stack

docker compose up -d

Check that both services are healthy:

docker compose ps

You should see both postgres and n8n with status “Up” and the postgres healthcheck showing “healthy.”

View Logs

Monitor startup and catch errors:

# All services
docker compose logs -f

# Just n8n
docker compose logs -f n8n

Why PostgreSQL Over SQLite

The default SQLite database stores everything in a single file. It works for testing but causes problems at scale:

  • Concurrent access fails: Multiple workflow executions corrupt the database
  • No replication: Your data exists in one place with zero redundancy
  • Performance degrades: Large execution histories slow everything down
  • Backups are risky: You cannot safely back up while n8n is running

PostgreSQL handles concurrent connections properly, supports point-in-time recovery, and scales with your workload. The extra setup takes 10 minutes and prevents catastrophic data loss.

For detailed database migration instructions, see our comprehensive self-hosting guide.


Essential Environment Variables

n8n’s behavior is controlled through environment variables. Here’s a complete reference for Docker deployments.

Core Configuration

VariableDescriptionExample
N8N_HOSTHostname for the n8n instancen8n.example.com
N8N_PORTPort n8n listens on5678
N8N_PROTOCOLProtocol (http or https)https
WEBHOOK_URLPublic URL for webhookshttps://n8n.example.com/
N8N_ENCRYPTION_KEY32-char key for credential encryptionabc123...
GENERIC_TIMEZONEDefault timezoneAmerica/New_York

Database Settings

VariableDescriptionDefault
DB_TYPEDatabase typesqlite
DB_POSTGRESDB_HOSTPostgreSQL hostname-
DB_POSTGRESDB_PORTPostgreSQL port5432
DB_POSTGRESDB_DATABASEDatabase name-
DB_POSTGRESDB_USERDatabase user-
DB_POSTGRESDB_PASSWORDDatabase password-

Execution Settings

VariableDescriptionDefault
EXECUTIONS_DATA_PRUNEAuto-delete old executionsfalse
EXECUTIONS_DATA_MAX_AGEHours to keep executions336
EXECUTIONS_DATA_PRUNE_MAX_COUNTMax executions to keep10000
EXECUTIONS_DATA_SAVE_ON_ERRORSave failed executionsall
EXECUTIONS_DATA_SAVE_ON_SUCCESSSave successful executionsall
N8N_CONCURRENCY_PRODUCTION_LIMITMax concurrent executions-1 (unlimited)

Performance Tuning

VariableDescriptionRecommendation
N8N_RUNNERS_ENABLEDEnable task runnerstrue
NODE_OPTIONSNode.js memory settings--max-old-space-size=2048
N8N_PAYLOAD_SIZE_MAXMax request body size16 (MB)

For high-volume deployments, enable queue mode to distribute execution across multiple workers.

Security Settings

VariableDescriptionProduction Value
N8N_BASIC_AUTH_ACTIVEEnable basic authtrue
N8N_BASIC_AUTH_USERAuth usernameYour choice
N8N_BASIC_AUTH_PASSWORDAuth passwordStrong password
N8N_SECURE_COOKIEUse secure cookiestrue (with HTTPS)

Important: Basic auth is deprecated in favor of n8n’s built-in user management. For new deployments, create an owner account through the setup wizard instead.


SSL/HTTPS Configuration

Production deployments require HTTPS. Exposing n8n over plain HTTP transmits credentials and session tokens in cleartext.

Traefik automatically obtains and renews SSL certificates from Let’s Encrypt. This is n8n’s officially recommended approach.

Update your docker-compose.yml:

services:
  traefik:
    image: traefik:v2.10
    restart: always
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
      - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
      - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - traefik_data:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: always
    labels:
      - traefik.enable=true
      - traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)
      - traefik.http.routers.n8n.tls=true
      - traefik.http.routers.n8n.entrypoints=web,websecure
      - traefik.http.routers.n8n.tls.certresolver=mytlschallenge
      - traefik.http.middlewares.n8n.headers.SSLRedirect=true
      - traefik.http.middlewares.n8n.headers.STSSeconds=315360000
      - traefik.http.middlewares.n8n.headers.browserXSSFilter=true
      - traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
      - traefik.http.middlewares.n8n.headers.forceSTSHeader=true
      - traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
      - traefik.http.middlewares.n8n.headers.STSPreload=true
      - traefik.http.routers.n8n.middlewares=n8n@docker
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${N8N_HOST}/
      # ... other environment variables
    # Remove the ports section - Traefik handles routing

volumes:
  traefik_data:

Add to your .env:

SSL_EMAIL=your-email@example.com
N8N_HOST=n8n.yourdomain.com

Option 2: Nginx Reverse Proxy

If you prefer Nginx or already have it running:

server {
    listen 80;
    server_name n8n.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name n8n.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/n8n.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.yourdomain.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;
        chunked_transfer_encoding off;
        proxy_buffering off;
        proxy_cache off;
    }
}

Use Certbot to obtain certificates:

certbot --nginx -d n8n.yourdomain.com

DNS Configuration

Point your domain’s A record to your server’s IP address. SSL certificate generation will fail if DNS is not properly configured.


Troubleshooting Common Issues

Docker deployments fail for predictable reasons. Here are the most common problems and their solutions.

Permission Denied Errors

Symptom: Container starts then immediately stops. Logs show permission errors.

Cause: n8n runs as user node (UID 1000). If Docker creates the volume directory with root ownership, n8n cannot write to it.

Fix:

# Find your volume location
docker volume inspect n8n_data

# Fix permissions (adjust path from inspect output)
sudo chown -R 1000:1000 /var/lib/docker/volumes/n8n_data/_data

For bind mounts instead of named volumes:

sudo chown -R 1000:1000 ./n8n_data

Port Already in Use

Symptom: Container fails to start with “port is already allocated” error.

Cause: Another application (or another n8n instance) is using port 5678.

Fix:

# Find what's using the port
lsof -i :5678

# Either stop that process or change n8n's port
# In docker-compose.yml:
ports:
  - "5679:5678"  # Map to different host port

Data Loss After Restart

Symptom: Workflows and credentials disappear when container restarts.

Cause: No persistent volume configured.

Fix: Always include a volume mount:

volumes:
  - n8n_data:/home/node/.n8n

For existing containers, export your workflows first (Settings > Export), then recreate with proper volumes.

JavaScript Heap Out of Memory

Symptom: Container crashes with memory allocation errors during workflow execution.

Cause: Workflows processing large datasets exceed Node.js memory limits.

Fix: Increase memory allocation:

environment:
  - NODE_OPTIONS=--max-old-space-size=4096

Also set container memory limits:

deploy:
  resources:
    limits:
      memory: 4G

If memory issues persist, consider breaking large workflows into smaller sub-workflows or enabling queue mode for distributed execution.

Cannot Connect to PostgreSQL

Symptom: n8n fails to start with database connection errors.

Cause: PostgreSQL container not ready, wrong credentials, or network issues.

Fix:

  1. Verify PostgreSQL is running: docker compose ps
  2. Check credentials match in .env
  3. Ensure depends_on with health check is configured
  4. Test connection manually:
docker compose exec postgres psql -U n8n -d n8n -c "SELECT 1"

Webhooks Not Triggering

Symptom: External services cannot reach your webhooks.

Cause: WEBHOOK_URL misconfigured, firewall blocking, or no public access.

Fix:

  1. Set WEBHOOK_URL to your publicly accessible URL
  2. Ensure port 443 (or 80) is open in your firewall
  3. Verify DNS resolves correctly
  4. Check SSL certificate is valid

For webhook debugging, use our workflow debugger tool.

OAuth Credentials Failing

Symptom: Google, Microsoft, or other OAuth integrations fail to authenticate.

Cause: Redirect URI mismatch between OAuth app configuration and n8n settings.

Fix:

  1. Set N8N_HOST and WEBHOOK_URL to your actual domain
  2. In your OAuth provider (Google Cloud Console, Azure, etc.), add the redirect URI: https://your-domain.com/rest/oauth2-credential/callback
  3. Restart n8n after changing environment variables

For detailed OAuth troubleshooting, see our authentication errors guide.


Upgrading n8n in Docker

Regular updates bring security patches, bug fixes, and new features. Here’s how to upgrade safely.

Check Current Version

docker compose exec n8n n8n --version

Pull the Latest Image

docker compose pull n8n

Or specify a version:

docker pull docker.n8n.io/n8nio/n8n:1.70.0

Recreate the Container

docker compose up -d n8n

Docker Compose automatically stops the old container and starts a new one with the updated image. Your data persists in the volume.

Version Pinning

For production stability, pin to specific versions instead of latest:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:1.70.0

This prevents unexpected changes from automatic pulls. Update deliberately after reviewing release notes.

Rollback if Needed

If an update causes problems:

# Stop the current container
docker compose down

# Change image version in docker-compose.yml to previous version
# Then start again
docker compose up -d

Your data remains intact because it lives in the volume, not the container.

Before Major Upgrades

  1. Back up your database: docker compose exec postgres pg_dump -U n8n n8n > backup.sql
  2. Export workflows: Download JSON exports from n8n’s UI
  3. Note your encryption key: You cannot recover credentials without it
  4. Test on staging first: Run the new version in a separate environment

Security Hardening

A default n8n installation is not secure. Apply these measures before exposing your instance to the internet.

Network Isolation

Keep database and internal services on a private network:

services:
  postgres:
    networks:
      - internal

  n8n:
    networks:
      - internal
      - external

networks:
  internal:
    internal: true
  external:

Only n8n connects to the external network. PostgreSQL remains inaccessible from outside.

Firewall Rules

On your host server, restrict access:

# Allow only necessary ports
ufw allow 22/tcp    # SSH
ufw allow 80/tcp    # HTTP (redirect to HTTPS)
ufw allow 443/tcp   # HTTPS
ufw enable

Block direct access to port 5678 if using a reverse proxy.

Authentication

n8n’s built-in user management requires creating an owner account on first access. For additional protection, add HTTP basic auth at the reverse proxy level.

Never expose n8n without authentication. The editor interface provides full access to credentials, code execution, and system commands.

Credential Encryption

Always set N8N_ENCRYPTION_KEY:

N8N_ENCRYPTION_KEY=your-32-character-encryption-key

Without this, credentials are encrypted with a default key that anyone can find in the source code. For advanced credential security patterns, see our credential management guide.

Disable Unnecessary Features

For internal tools that don’t need public templates:

environment:
  - N8N_TEMPLATES_ENABLED=false
  - N8N_VERSION_NOTIFICATIONS_ENABLED=false
  - N8N_DIAGNOSTICS_ENABLED=false

Security Headers

If using Nginx instead of Traefik, add security headers:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

For a complete security checklist, refer to our self-hosting guide.


Monitoring Your Deployment

Production instances need visibility. Know when things break before users report them.

Health Checks

Add a health check to your n8n service:

services:
  n8n:
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:5678/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3

Enable Metrics

n8n exposes Prometheus-compatible metrics:

environment:
  - N8N_METRICS=true
  - N8N_METRICS_INCLUDE_DEFAULT_METRICS=true
  - N8N_METRICS_INCLUDE_QUEUE_METRICS=true

Access metrics at http://your-n8n:5678/metrics.

Error Alerting

Create a workflow that triggers on execution failures and sends notifications to Slack, email, or your preferred channel. The Error Trigger node captures failures from any workflow.

Log Aggregation

Forward container logs to a central system:

docker compose logs -f | tee -a /var/log/n8n.log

Or configure Docker’s logging driver to ship to Datadog, Loki, or CloudWatch.


When to Go Beyond Single Instance

Docker handles most n8n workloads beautifully. Consider upgrading when you hit these limits:

Signs you need queue mode:

  • Workflows timing out during high traffic
  • Memory errors from concurrent executions
  • UI becoming sluggish during execution spikes

Our queue mode guide covers multi-worker setups with Redis.

Signs you need Kubernetes:

  • Managing multiple n8n instances across teams
  • Auto-scaling requirements
  • Complex networking and service mesh needs

Signs you need managed services:

  • No DevOps expertise on the team
  • Compliance requirements you cannot meet in-house
  • Preference for operational simplicity over cost savings

If infrastructure management isn’t your core competency, our self-hosted setup service handles deployment, security, and ongoing maintenance.


Frequently Asked Questions

Can I run n8n Docker on Windows?

Yes. Install Docker Desktop for Windows and use WSL 2 backend for best performance. The Docker Compose configurations in this guide work identically on Windows. Avoid using Windows paths for bind mounts. Use named volumes instead for simpler permission handling.

How do I migrate from SQLite to PostgreSQL in Docker?

Export your workflows from n8n’s UI as JSON files. Create a new deployment with PostgreSQL configured. Import the workflows into the new instance and re-add your credentials. For complex migrations with many workflows, consider our setup service which includes database migration assistance.

What’s the best VPS provider for n8n Docker?

Any provider supporting Docker works: DigitalOcean, Hetzner, Linode, Vultr, AWS Lightsail. For cost-effective hosting, Hetzner offers excellent specs per dollar. Start with a $10-20/month instance (2 cores, 4GB RAM) and scale as needed. Avoid shared CPU instances for production workloads.

How do I back up my Docker n8n instance?

Back up three things: the PostgreSQL database, the n8n data volume, and your .env file. For PostgreSQL: docker compose exec postgres pg_dump -U n8n n8n > backup.sql. For the volume, copy the entire volume contents or use Docker’s volume backup commands. Automate this with cron and upload to S3 or similar. Test restores regularly.

Why are my workflows not persisting after container updates?

If you’re losing data, your volume mount is misconfigured. Ensure n8n_data:/home/node/.n8n appears in your compose file under the n8n service’s volumes. Named volumes (like n8n_data) persist across container recreation. If using bind mounts, check directory permissions allow UID 1000 to write.


Next Steps

You now have everything needed to deploy n8n with Docker. Start with the basic setup for testing, then graduate to the production Docker Compose configuration for real workloads.

Recommended reading:

For the official Docker documentation, see the n8n Docker installation guide and the Docker Hub page.

If you’d rather skip the infrastructure work entirely, our n8n setup service delivers a production-ready deployment configured for your specific needs.

Ready to Automate Your Business?

Tell us what you need automated. We'll build it, test it, and deploy it fast.

âś“ 48-72 Hour Turnaround
âś“ Production Ready
âś“ Free Consultation
⚡

Create Your Free Account

Sign up once, use all tools free forever. We require accounts to prevent abuse and keep our tools running for everyone.

or

You're in!

Check your email for next steps.

By signing up, you agree to our Terms of Service and Privacy Policy. No spam, unsubscribe anytime.

🚀

Get Expert Help

Add your email and one of our n8n experts will reach out to help with your automation needs.

or

We'll be in touch!

One of our experts will reach out soon.

By submitting, you agree to our Terms of Service and Privacy Policy. No spam, unsubscribe anytime.