← Back to Home

n8n 工作流自动化 Self-Hosted Docker 容器化部署 Deployment Troubleshooting Guide

Dockern8nself-hostedPostgreSQLautomation

n8n is an open-source workflow automation tool with excellent Docker self-hosting support. When I deployed n8n on an Ubuntu 开发环境 24.04 VPS 配置避坑, it took me 2 full days to get a production-ready setup. This article documents the 5 traps I hit and how I resolved each one.

All information is verified against the official n8n documentation (docs.n8n.io) and my actual test environment.

Trap 1: SQLite to PostgreSQL Migration — Missing the Key Environment Variable

n8n defaults to SQLite, storing data in a database file under /home/node/.n8n. When I tried switching to PostgreSQL, I configured the database connection in docker-compose.yml, but the container kept throwing ECONNREFUSED.

My broken configuration:

environment:
  - DB_TYPE=postgres
  - DB_POSTGRESDB=n8n
  - DB_POSTGRESHOST=postgres
  - DB_POSTGRESUSER=n8n
  - DB_POSTGRESPASSWORD=xxx

Container log error:

Database is locked
Cannot start n8n

Root cause: n8n's documentation clearly states that when using PostgreSQL, you **must** also set the DB_ADAPTER variable — otherwise n8n ignores all other DB variables and continues trying to read/write SQLite, locking the database file.

Correct configuration:

environment:
  - DB_TYPE=postgresdb
  - DB_ADAPTER=postgres
  - DB_POSTGRESDB=n8n
  - DB_POSTGRESHOST=postgres
  - DB_POSTGRESUSER=n8n
  - DB_POSTGRESPASSWORD=${DB_PASSWORD}
  - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}

**Key point**: DB_ADAPTER=postgres is a documented requirement, yet many third-party tutorials omit it entirely.

Additionally, the PostgreSQL container's startup order matters — n8n must wait for PostgreSQL to be fully ready before starting. Add depends_on with a health check:

postgres:
  image: postgres:16-alpine
  environment:
    POSTGRES_DB: n8n
    POSTGRES_USER: n8n
    POSTGRES_PASSWORD: ${DB_PASSWORD}
  volumes:
    - n8n_pgdata:/var/lib/postgresql/data
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U n8n"]
    interval: 10s
    timeout: 5s
    retries: 5

n8n:
  image: docker.n8n.io/n8nio/n8n
  depends_on:
    postgres:
      condition: service_healthy

Trap 2: Scheduled Tasks Not Running — Inconsistent Timezone Configuration

I set up a Schedule Trigger to run every morning at 9 AM, but it actually executed at 1 AM — an 8-hour offset.

The issue: Docker containers have an isolated timezone from the host. I had set Asia/Shanghai on the host machine, but the TZ environment variable wasn't properly passed into the container.

**Wrong approach**: Setting only GENERIC_TIMEZONE in docker-compose.yml, ignoring the container's internal system timezone.

Correct approach — set both environment variables:

environment:
  - GENERIC_TIMEZONE=Asia/Shanghai
  - TZ=Asia/Shanghai

GENERIC_TIMEZONE controls n8n's internal scheduler calculations. TZ controls the container system's timezone. They must match, or scheduled triggers will fire at the wrong time.

Verification method — check actual time inside the container:

docker exec -it n8n date

Trap 3: Webhooks Not Found Behind Reverse Proxy — N8N_WEBHOOK_URL Must Be Set

Running n8n on port 5678, proxied through Nginx at n8n.example.com. The web UI loaded fine, but creating a Webhook node and visiting the trigger URL returned 404.

Nginx configuration:

location / {
    proxy_pass http://127.0.0.1:5678;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

Problem: Without an explicit N8N_WEBHOOK_URL, n8n defaults to http://localhost:5678, causing relative path errors when accessed through Nginx.

Solution: Explicitly set the Webhook URL in docker-compose.yml:

environment:
  - N8N_WEBHOOK_URL=https://n8n.example.com/
  - WEBHOOK_URL=https://n8n.example.com/

Note: the trailing slash on the URL matters. Restart the container after setting this:

docker compose down && docker compose up -d

Trap 4: Workflow Execution Timeout — Long Workflows Mysteriously Die

A data sync workflow that needs 5 minutes kept dying around the 30-second mark. Log output:

Execution timed out after 30 seconds

n8n's default execution timeout is 30 seconds. Workflows processing large files or calling slow external APIs commonly hit this limit.

Fix: Adjust the timeout via environment variable, or override per-workflow:

environment:
  # Default execution timeout in milliseconds — 10 minutes here
  - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
  # Disable timeout entirely (use carefully in production)
  - N8N_EXECUTIONS_TIMEOUT=0
  # Or set a specific timeout (300000ms = 5 minutes)
  - N8N_EXECUTIONS_TIMEOUT=300000

The safer approach is setting timeout per-workflow rather than disabling it globally. Open the workflow → Settings → Execution Settings → Custom Timeout.

Trap 5: Credentials Lost After Restart — N8N_ENCRYPTION_KEY Must Be Persisted

n8n encrypts all credentials (API keys, passwords, etc.) stored in the database using the key specified by the N8N_ENCRYPTION_KEY environment variable. After restarting the container, all previously configured API credentials stopped working.

Cause: N8N_ENCRYPTION_KEY wasn't persisted. If docker-compose doesn't properly pass this variable on restart, n8n generates a new random key, making it impossible to decrypt existing credentials.

**Correct approach**: Store the encryption key in a .env file (keep it secure) and reference it in docker-compose.yml:

# .env file
N8N_ENCRYPTION_KEY=your-very-long-random-string-here-min-32-chars
DB_PASSWORD=your-postgres-password
ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
environment:
  - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}

Generate a strong random key:

openssl rand -hex 32

**Warning**: Changing N8N_ENCRYPTION_KEY will make all existing credentials unreadable. To rotate the key, export all credentials first and re-import after the change.

Complete Correct docker-compose.yml

Here's my final production-ready configuration that addresses all traps:

version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - n8n_pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  n8n:
    image: docker.n8n.io/n8nio/n8n
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - GENERIC_TIMEZONE=Asia/Shanghai
      - TZ=Asia/Shanghai
      - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
      - N8N_RUNNERS_ENABLED=true
      - N8N_WEBHOOK_URL=https://n8n.example.com/
      - DB_TYPE=postgresdb
      - DB_ADAPTER=postgres
      - DB_POSTGRESDB=n8n
      - DB_POSTGRESHOST=postgres
      - DB_POSTGRESUSER=n8n
      - DB_POSTGRESPASSWORD=${DB_PASSWORD}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_EXECUTIONS_TIMEOUT=300000
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

volumes:
  n8n_data:
  n8n_pgdata:

Companion Nginx reverse proxy (critical settings):

location / {
    proxy_pass http://127.0.0.1:5678;
    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;
    # WebSocket support — required for n8n editor real-time updates
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Who Is This For

Good candidates for n8n self-hosting:

Not suitable:

Cost Reference

Minimum cost for self-hosted n8n:

OptionMonthly CostSpecs
VPS (minimum)$6-101C1G
VPS (recommended)$12-202C2G
Add-on: PostgreSQL$0-5Built-in Docker or cloud DB

Compared to Zapier free (100 executions/month), self-hosted n8n has unlimited executions, and a $12/month VPS offers far better value.

If you're using AI APIs to power n8n workflows — generating content, classifying data, or processing text — the n8n + MiniMax API combination is one of the most cost-effective self-hosted setups available.

👉 Get started now: https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link

Summary: Pre-Deployment Checklist

Before deploying n8n via Docker, confirm each item:

If your workflows need to call AI APIs to generate content, classify data, or process text, the n8n + MiniMax API combination delivers the best cost-to-performance ratio for self-hosted automation.

👉 Get started now: https://platform.minimaxi.com/subscribe/token-plan?code=E5yur9NOub&source=link

🔗 Related Tech Articles

Deep dive into related technical topics:

n8n Self-Hosted Docker Deployment Troubleshooting Guide
技术标签: n8n, self-hosted
n8n Self-Hosted Docker Deployment Troubleshooting Guide
技术标签: n8n, self-hosted
n8n Self-Hosted Configuration Pitfalls
技术标签: n8n, self-hosted
⚡ Automation Workflow Hardware
查看推荐 →