Deploy n8n with Docker Compose: Complete Setup Guide
Table of Contents
n8n Deployment: Docker Compose Setup
Part 1 of 3. Part 2: Kubernetes Deployment | Part 3: Configuration Deep Dive
I once watched a startup lose three days of sales leads because their Zapier integration hit a rate limit during a product launch. The webhook queue backed up, credentials expired silently, and nobody noticed until the CRM team asked why Mondayβs leads never arrived. That single incident cost them more in lost revenue than a self-hosted automation platform would have cost to run for an entire year.
That is why I run n8n, an open-source workflow automation tool, on my own infrastructure. n8n gives you the visual, node-based workflow builder that tools like Zapier and Make offer, but with one critical difference: you control the data, the credentials, and the execution environment. In 2025, with AI Agent nodes, LLM integrations, and self-hosted LLM support, n8n has evolved from a simple automation tool into a legitimate platform for AI-powered orchestration.
In this guide, I will walk you through deploying n8n in two configurations: Docker Compose for single-node setups and Kubernetes for production workloads. I will cover webhook security, credential encryption, queue mode with Redis, a PostgreSQL backend, and how to wire up AI Agent nodes with your own LLMs.
What You Will Build
By the end of this guide, you will have:
- A running n8n instance with a PostgreSQL database backend
- Redis-backed queue mode for handling concurrent executions
- Encrypted credential storage and secure webhook endpoints
- An AI Agent workflow connected to a self-hosted LLM
- Kubernetes manifests you can drop into a GitOps pipeline
Prerequisites
Before you start, make sure you have the following in place.
| Requirement | Minimum | Recommended | Verify Command |
|---|---|---|---|
| OS | Ubuntu 22.04 | Ubuntu 24.04 LTS | lsb_release -a |
| CPU | 2 cores | 4+ cores | nproc |
| RAM | 4 GB | 8 GB | free -h |
| Docker | 24.0 | 27.0+ | docker --version |
| Docker Compose | 2.20 | 2.27+ | docker compose version |
| Kubernetes | 1.28 | 1.30+ (optional) | kubectl version |
| GPU | None | NVIDIA A10G (for local LLMs) | nvidia-smi |
Version note: These instructions apply to n8n 1.50+. If you are running an older version, some environment variable names may differ.
Architecture Overview
n8n can run in two execution modes: regular (everything in one process) and queue (webhooks and workers separated). For anything beyond personal experimentation, I strongly recommend queue mode.
In queue mode, the architecture looks like this:
βββββββββββββββ βββββββββββββββ ββββββββββββββββ Client ββββββΆβ n8n Web ββββββΆβ PostgreSQL ββ (Browser) β β (Main) β β (Database) ββββββββββββββββ ββββββββ¬βββββββ βββββββββββββββ β ββββββββββββββΌβββββββββββββ βΌ βΌ βΌ βββββββββββ βββββββββββ βββββββββββ β Worker 1β β Worker 2β β Worker Nβ ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ βββββββββββββββΌββββββββββββββ βΌ βββββββββββββββ β Redis β β (Queue) β βββββββββββββββThe main process handles the web UI, webhook ingress, and API requests. Workers pick up execution jobs from Redis. PostgreSQL persists workflows, executions, and credentials. This separation means you can scale workers independently of the web tier, and a crashing workflow will not take down the UI.
Docker Compose Deployment
Docker Compose is the fastest path to a working n8n instance. I use this setup for homelab environments, staging, and smaller production workloads under moderate concurrency.
Step 1: Create the Project Directory
mkdir -p ~/n8n-deployment/{postgres,redis}cd ~/n8n-deploymentStep 2: Write the Docker Compose File
Create docker-compose.yml with the following configuration. I have annotated each section so you know what it does.
# Production-ready n8n with PostgreSQL, Redis, and encryption
services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_USER: n8n POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: n8n volumes: - ./postgres/data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U n8n"] interval: 10s timeout: 5s retries: 5 networks: - n8n-network
redis: image: redis:7-alpine restart: unless-stopped command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - ./redis/data:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 5 networks: - n8n-network
n8n: image: n8nio/n8n:1.50 restart: unless-stopped environment: # Database DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: n8n DB_POSTGRESDB_USER: n8n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
# Queue mode EXECUTIONS_MODE: queue QUEUE_BULL_REDIS_HOST: redis QUEUE_BULL_REDIS_PORT: 6379
# Security N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} WEBHOOK_URL: ${WEBHOOK_URL}
# General GENERIC_TIMEZONE: UTC TZ: UTC N8N_BASIC_AUTH_ACTIVE: "true" N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} ports: - "5678:5678" depends_on: postgres: condition: service_healthy redis: condition: service_healthy volumes: - ./n8n-data:/home/node/.n8n networks: - n8n-network
# Optional: Add workers for concurrent execution n8n-worker: image: n8nio/n8n:1.50 restart: unless-stopped command: worker environment: DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: n8n DB_POSTGRESDB_USER: n8n DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} QUEUE_BULL_REDIS_HOST: redis QUEUE_BULL_REDIS_PORT: 6379 N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} GENERIC_TIMEZONE: UTC depends_on: - redis - postgres deploy: replicas: 2 networks: - n8n-network
networks: n8n-network: driver: bridgeStep 3: Create the Environment File
Create .env in the same directory. Never commit this file to Git.
# .env: keep this secretPOSTGRES_PASSWORD=$(openssl rand -base64 32)N8N_ENCRYPTION_KEY=$(openssl rand -base64 32)N8N_BASIC_AUTH_USER=adminN8N_BASIC_AUTH_PASSWORD=$(openssl rand -base64 24)WEBHOOK_URL=https://n8n.yourdomain.com/Generate secure values:
touch .envchmod 600 .envcat <<EOF > .envPOSTGRES_PASSWORD=$(openssl rand -base64 32)N8N_ENCRYPTION_KEY=$(openssl rand -base64 32)N8N_BASIC_AUTH_USER=adminN8N_BASIC_AUTH_PASSWORD=$(openssl rand -base64 24)WEBHOOK_URL=https://n8n.yourdomain.com/EOFStep 4: Start the Stack
docker compose up -dWait about 30 seconds for the database migrations to run, then verify:
docker compose ps# All services should show "healthy" or "running"
docker compose logs n8n | grep "Editor is now accessible"# Expected output: "Editor is now accessible via: http://localhost:5678/"Navigate to http://localhost:5678 and complete the initial setup wizard.
Common Issues
- Port 5678 already in use: Change the host port mapping to
8080:5678indocker-compose.yml. - Permission denied on volumes: n8n runs as user
node(UID 1000). Ensure the host directories are writable:sudo chown -R 1000:1000 ./n8n-data.
FAQ
What is n8n and how does it differ from Zapier?
n8n is an open-source workflow automation platform. Unlike Zapier, you self-host it, which means you own your data, your credentials never leave your infrastructure, and you are not bound by usage tiers or rate limits. n8n uses a visual node-based editor that I find more flexible for building complex multi-step automation pipelines.
Do I need queue mode for a small deployment?
For a handful of workflows running occasionally, regular mode works fine. I switch to queue mode as soon as I have more than 5 active workflows or any workflow that uses webhooks in production. Queue mode prevents webhook timeouts by decoupling request acceptance from execution.
Can I run n8n with SQLite instead of PostgreSQL?
Yes, n8n ships with SQLite by default. Do not use it in production. SQLite does not handle concurrent writes. I learned this the hard way when a busy webhook corrupted the database during a traffic spike. PostgreSQL handles concurrent connections properly and makes backups trivial with pg_dump.
How do I secure the n8n editor UI?
Enable basic authentication by setting N8N_BASIC_AUTH_ACTIVE=true with a strong password. I also put n8n behind a reverse proxy with TLS and restrict access by IP range where possible. Never expose the editor to the public internet without authentication.
What happens if the PostgreSQL database goes down?
n8n stops processing workflows until the database recovers. Workers will queue jobs in Redis memory, but once Redis hits its maxmemory limit, it starts evicting old jobs. This is why I run PostgreSQL with a replica and automated backups.
Can I connect n8n to Ollama or vLLM for local AI?
Yes. Both Ollama and vLLM expose OpenAI-compatible APIs. Point the HTTP Request node or the AI Agent nodeβs chat model at your local LLM service URL. I cover this setup in detail in Part 2.
Parts in this series: Part 2: Kubernetes Deployment β | Part 3: Configuration Deep Dive β