n8n is an open-source workflow automation platform that connects apps, APIs, databases, and AI models through a visual, node-based editor. Unlike closed automation tools that charge per execution or cap your workflow count, self-hosting n8n on your own dedicated server gives you unlimited executions, full data control, and direct access to the underlying infrastructure your AI workflows need—persistent storage, GPU access for local models, and no third-party rate limits.
This guide walks through a complete, production-ready installation: Docker-based setup, PostgreSQL as the database, task runners running in isolated external mode (n8n's recommended production configuration since 2.0), a reverse proxy with a real SSL certificate, host firewall rules, the environment variables that actually matter, and the extra steps required if you plan to run AI-driven workflows (local LLMs, vector stores, LangChain-style agent nodes) rather than just simple app-to-app automations.
Tutorial Contents
Prerequisites & System Requirements
Step 1: Lock down the host firewall first
Step 2: Install Docker on the server
Step 3: Quick test run (optional but recommended)
Step 4: Production setup with Docker Compose & Postgres
Step 5: Reverse proxy and SSL with Nginx and Let's Encrypt
Step 6: Environment variables for AI workflows
Step 7: Connecting n8n to AI tools
Step 8 & 9: Updating n8n and Backups
Security Checklist & Troubleshooting
Prerequisites
System Requirements
- A dedicated or virtual server running Ubuntu 22.04 or 24.04 LTS (or another modern Linux distro).
- Light workload: 2 vCPUs and 4 GB RAM.
- AI Workflows: 4+ vCPUs and 8 GB+ RAM (especially for local embedding, external task runners, or high concurrent webhooks).
Network & Access Requirements
- A registered domain name pointing an A record to your server's IP.
- Root or sudo access via SSH, with key-based authentication (not password login).
- Ports 80 and 443 open on your firewall for HTTP/HTTPS. No other port needs to be public.
1 Lock down the host firewall first
Before installing anything, set a default-deny firewall so nothing you spin up later is accidentally reachable from the internet:
sudo apt update
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
This is a deliberate addition beyond just binding container ports to 127.0.0.1. Compose files get edited over time, and a firewall gives you a second layer of protection if a future change accidentally publishes a port (n8n on 5678, Ollama on 11434, the task broker on 5679) to 0.0.0.0.
2 Install Docker on the server
n8n's documentation recommends Docker for most self-hosting scenarios because it isolates n8n's runtime and dependencies from the host OS, and it makes upgrades and database management simpler.
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verify the install:
docker --version
docker compose version
Optionally, add your user to the docker group so you don't need sudo for every command:
sudo usermod -aG docker $USER
newgrp docker
sudo docker ... on shared or less-trusted boxes.
3 Quick test run (optional but recommended)
Before building the full production stack, confirm n8n runs correctly with a minimal container:
docker volume create n8n_data
docker run -it --rm \
--name n8n \
-p 127.0.0.1:5678:5678 \
-e GENERIC_TIMEZONE="America/Detroit" \
-e TZ="America/Detroit" \
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
-v n8n_data:/home/node/.n8n \
docker.n8n.io/n8nio/n8n
Replace America/Detroit with your own IANA timezone. TZ sets the container's system timezone (what commands like date return), while GENERIC_TIMEZONE controls the timezone n8n uses for schedule-based nodes.
To actually view the setup screen from your local machine, open an SSH tunnel: ssh -L 5678:localhost:5678 user@your-server-ip, then visit http://localhost:5678.
Stop the container with Ctrl+C — it self-removes because of --rm — and move on to the production setup below.
Production Stack & Reverse Proxy
4 Production setup with Docker Compose, PostgreSQL, and external task runners
For anything beyond testing, n8n should run behind Docker Compose with PostgreSQL instead of the default SQLite database. This handles concurrent writes perfectly for chained AI workflows. We will also run task runners in external mode for proper sandbox isolation.
Create a project directory and the environment file:
mkdir -p ~/n8n-stack && cd ~/n8n-stack
nano .env
Populate .env:
# Domain and protocol
DOMAIN_NAME=yourdomain.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=America/Detroit
# Postgres credentials — change these
POSTGRES_USER=n8n
POSTGRES_PASSWORD=change_this_to_a_strong_password
POSTGRES_DB=n8n
# n8n encryption key — generate with: openssl rand -hex 24
N8N_ENCRYPTION_KEY=replace_with_generated_key
# Task runner shared secret — generate with: openssl rand -hex 32
N8N_RUNNERS_AUTH_TOKEN=replace_with_generated_token
Generate the two secrets rather than leaving placeholders — the encryption key protects every stored credential, and the runner token authenticates the task-runner sidecar to n8n's task broker:
openssl rand -hex 24 # N8N_ENCRYPTION_KEY
openssl rand -hex 32 # N8N_RUNNERS_AUTH_TOKEN
Now create docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
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 -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
n8n:
image: docker.n8n.io/n8nio/n8n:2.34.3
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- 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}
# Task runners — external mode (recommended for production)
- N8N_RUNNERS_MODE=external
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN}
# Hardening
- N8N_BLOCK_ENV_ACCESS_IN_NODE=true
- N8N_RESTRICT_FILE_ACCESS_TO=/home/node/.n8n-files
volumes:
- n8n_data:/home/node/.n8n
- n8n_files:/home/node/.n8n-files
depends_on:
postgres:
condition: service_healthy
task-runner:
image: docker.n8n.io/n8nio/runners:2.34.3
restart: unless-stopped
environment:
- N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN}
depends_on:
- n8n
volumes:
postgres_data:
n8n_data:
n8n_files:
Bring the stack up:
docker compose up -d
docker compose ps
All three containers should show as running/healthy within about a minute. Check docker compose logs n8n and docker compose logs task-runner to confirm the runner connected to the broker successfully.
5 Reverse proxy and SSL with Nginx and Let's Encrypt
With n8n bound only to localhost, install Nginx and Certbot on the host to terminate HTTPS and forward traffic to the container:
sudo apt install -y nginx certbot python3-certbot-nginx
Create an Nginx server block:
sudo nano /etc/nginx/sites-available/n8n
server {
listen 80;
server_name n8n.yourdomain.com;
location / {
proxy_pass http://127.0.0.1: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;
proxy_cache_bypass $http_upgrade;
client_max_body_size 50M;
# Basic hardening headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
}
The Upgrade/Connection headers are required because n8n's editor UI uses WebSocket connections for real-time execution updates. The raised client_max_body_size matters for AI workflows passing larger payloads (like document text) going into an embedding or summarization node.
Enable the site and get a certificate:
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d n8n.yourdomain.com
Visit https://n8n.yourdomain.com and you should land on n8n's setup screen to create the owner account. Enable two-factor authentication for this account immediately from the instance's Security settings.
Advanced Configuration for AI Tools
6 Environment variables worth knowing for AI workflows
Beyond the basics above, several environment variables matter specifically when n8n is running AI-oriented automations:
| Variable | Purpose |
|---|---|
| N8N_PAYLOAD_SIZE_MAX | Raises the max request body size n8n itself will accept, in MB — relevant if workflows pass large documents or images to AI nodes. |
| EXECUTIONS_TIMEOUT | Default execution timeout in seconds. Long-running AI chains (multi-step agent reasoning, large batch embedding jobs) can exceed the default and need this raised. |
| N8N_RUNNERS_MODE | Already set to external above — isolates code execution from the main n8n process. |
| EXECUTIONS_DATA_PRUNE EXECUTIONS_DATA_MAX_AGE |
Controls automatic cleanup of execution history. AI workflows that log large model outputs on every run can bloat the database quickly without this. |
| N8N_METRICS | Enables a Prometheus-compatible metrics endpoint, useful for monitoring resource usage if you're running local model inference alongside n8n. |
| N8N_BLOCK_ENV_ACCESS_IN_NODE | Already set above — blocks Code node access to process.env. |
| N8N_RESTRICT_FILE_ACCESS_TO | Already set above — confines file-system nodes to one directory. |
| NODES_EXCLUDE | The ExecuteCommand and LocalFileTrigger nodes are disabled by default as of 2.0. Only re-enable specific nodes here if you have a real need. |
7 Connecting n8n to AI tools
For a fully self-hosted AI stack (no external API calls), the common pattern is to run an inference server such as Ollama alongside n8n. You can simply add it to your docker-compose.yml:
ollama:
image: ollama/ollama
restart: unless-stopped
ports:
- "127.0.0.1:11434:11434"
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
Add ollama_data under volumes, and n8n's dedicated Ollama nodes can then reach it at http://ollama:11434 over the internal Docker network. GPU passthrough requires the NVIDIA Container Toolkit installed on the host.
8 Updating n8n
Because the stack uses Docker Compose with pinned versions, updates are deliberate. Edit docker-compose.yml to bump both the n8n and task-runner image tags to the same new version, then run:
cd ~/n8n-stack
docker compose pull
docker compose up -d
9 Backups
Two things need backing up: the PostgreSQL database and the n8n_data volume:
# Database dump
docker compose exec postgres pg_dump -U n8n n8n > n8n-backup-$(date +%F).sql
# Volume backup
docker run --rm -v n8n-stack_n8n_data:/data -v $(pwd):/backup alpine \
tar czf /backup/n8n-data-$(date +%F).tar.gz -C /data .
Checklists & Troubleshooting
Security checklist before going live
- Host firewall (ufw) default-denies incoming traffic; only SSH, 80, and 443 are open.
- Every container port (n8n, postgres, task-runner, ollama) is bound to 127.0.0.1 or not published at all — never 0.0.0.0.
- Task runners run in external mode with a unique, generated N8N_RUNNERS_AUTH_TOKEN.
- N8N_BLOCK_ENV_ACCESS_IN_NODE=true and N8N_RESTRICT_FILE_ACCESS_TO are set.
- N8N_ENCRYPTION_KEY is a strong, generated value, backed up separately from the server.
- ExecuteCommand and LocalFileTrigger nodes stay disabled.
- The owner account has 2FA enabled; teammates use scoped project roles.
- SSH access uses key-based authentication only.
- EXECUTIONS_DATA_PRUNE=true is active to prevent database bloat from AI model outputs.
Troubleshooting common issues
- n8n container won't start: Check
docker compose logs n8n. This is most often a database connection failure — confirm postgres is healthy first. - Task runner won't connect / Code nodes fail: Check
docker compose logs task-runner. Confirm N8N_RUNNERS_AUTH_TOKEN matches exactly between n8n and the runner, and both images are on the identical version. - Webhooks return localhost: WEBHOOK_URL isn't set, or doesn't match N8N_HOST/N8N_PROTOCOL. Update the env block and restart.
- Execution status never updates: Nginx is missing the WebSocket upgrade headers shown in Step 5.
- SSL certificate renewal fails: Run
sudo certbot renew --dry-runto test manually and check for DNS/firewall issues blocking port 80.
Summary & Build Your Automation Server
A production n8n install on a dedicated server comes down to six real components: Docker running the n8n, PostgreSQL, and task-runner containers, a host firewall restricting everything to SSH/80/443, a reverse proxy handling HTTPS, correctly set environment variables, a backup routine, and — for AI-specific workflows — a local inference service like Ollama sitting on the same Docker network so model calls never leave the server.
This setup scales from a single-user automation box to a multi-workflow AI backend simply by adjusting server resources, since the underlying architecture doesn't change.
Are you looking for the perfect bare-metal foundation for your heavy AI workloads? Explore our powerful, unmetered servers today.
Explore Dedicated Servers →Discover Leo Servers High-Performance Locations
Leo Servers operates premium bare-metal environments worldwide, offering diverse hosting options. Check out our specialized offerings to choose the setup that best suits your intensive workload needs.
