n8n on a VPS: Self-Hosted Workflow Automation with 400+ Integrations and No-Code Flows
n8n is an open-source workflow automation platform with 400+ integrations — Slack, GitHub, Notion, Google Sheets, Postgres, Stripe, Mailgun, OpenAI, and hundreds more — connected through a visual node-based editor. n8n Cloud costs $20–$50/month. Self-hosting on a VPS provides unlimited executions and workflows, custom code nodes, and private data processing at infrastructure cost.
n8n vs Zapier vs Make (Integromat)
- n8n (self-hosted): Unlimited executions, code nodes for custom logic, complex branching, self-managed. Developer-friendly.
- Zapier: Easiest to use, 5,000+ apps, $20–$100/month, limited executions per plan
- Make (Integromat): More powerful than Zapier, visual data mapping, $9–$29/month
- Choose n8n: Developer teams, sensitive data that shouldn’t leave your infrastructure, complex workflows with custom code, or cost-consciousness at high execution volumes
Step 1: Docker Compose with PostgreSQL
<code">mkdir -p /opt/n8n && cd /opt/n8n nano docker-compose.yml
<code">version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- N8N_HOST=n8n.yourdomain.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.yourdomain.com/
- N8N_BASIC_AUTH_ACTIVE=false # Using n8n's own auth instead
- N8N_USER_MANAGEMENT_JWT_SECRET=${JWT_SECRET}
# Database — PostgreSQL (recommended for production)
- DB_TYPE=postgresdb
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_HOST=n8n-postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# Timezone
- GENERIC_TIMEZONE=UTC
- TZ=UTC
# Execution settings
- EXECUTIONS_PROCESS=main
- EXECUTIONS_DATA_SAVE_ON_ERROR=all
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=none # Don't save all — saves disk
- EXECUTIONS_DATA_MAX_AGE=168 # Hours (7 days)
volumes:
- n8n_data:/home/node/.n8n
depends_on:
n8n-postgres:
condition: service_healthy
n8n-postgres:
image: postgres:16-alpine
container_name: n8n-postgres
restart: always
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- n8n_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
volumes:
n8n_data:
n8n_db:
<code">cat > .env << 'EOF' POSTGRES_PASSWORD=StrongN8nDbPassword! JWT_SECRET=$(openssl rand -hex 32) EOF chmod 600 .env docker compose up -d docker compose logs -f n8n
Step 2: Nginx Reverse Proxy
<code">sudo nano /etc/nginx/sites-available/n8n
<code">server {
listen 80;
server_name n8n.yourdomain.com;
return 301 https://$host$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;
client_max_body_size 50M;
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_read_timeout 300s;
}
}
<code">sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/ sudo certbot --nginx -d n8n.yourdomain.com sudo systemctl reload nginx
Step 3: Initial Setup
- Visit
https://n8n.yourdomain.com - Create owner account (first user)
- Settings → Users → invite team members
- Credentials → store API keys (GitHub token, Slack bot token, OpenAI key, etc.)
Step 4: Build Example Workflows
Workflow 1: GitHub Issue → Slack Notification
<code"># Nodes: # 1. Webhook trigger (receives GitHub webhook on issue events) # 2. IF node: filter for "opened" or "labeled:urgent" issues # 3. Slack node: post message to #engineering channel # 4. Notion node: create database entry for tracking # GitHub Webhook URL: https://n8n.yourdomain.com/webhook/github-issues # Add in GitHub repo → Settings → Webhooks → Payload URL
Workflow 2: Daily Database Report → Email
<code"># Nodes: # 1. Schedule trigger: cron "0 9 * * 1-5" (9 AM weekdays) # 2. PostgreSQL node: run query "SELECT count(*) as new_users FROM users WHERE created_at > NOW()-INTERVAL '1 day'" # 3. Code node: format the data into a readable string # 4. Gmail/Mailgun node: send report email to team
Workflow 3: AI-Powered Slack Bot
<code"># Nodes: # 1. Slack trigger: listens for @mentions or DMs to the bot # 2. HTTP Request node: POST to OpenAI API with message as prompt # 3. Slack node: reply with AI response in the same thread # In Slack App settings → Event Subscriptions → Request URL: # https://n8n.yourdomain.com/webhook/slack-bot
Step 5: Custom Code Nodes
n8n’s Code node executes JavaScript with access to all input data:
<code"># Example Code node — transform data
const items = $input.all();
return items.map(item => {
const data = item.json;
return {
json: {
id: data.id,
email: data.email.toLowerCase(),
full_name: `${data.first_name} ${data.last_name}`.trim(),
created_date: new Date(data.created_at).toISOString().split('T')[0],
is_premium: data.plan === 'pro' || data.plan === 'enterprise',
}
};
});
Step 6: Webhooks for External Triggers
<code"># n8n generates webhook URLs automatically when you add a Webhook node
# Format: https://n8n.yourdomain.com/webhook/YOUR-UUID
# Test your webhook:
curl -X POST https://n8n.yourdomain.com/webhook/YOUR-UUID \
-H "Content-Type: application/json" \
-d '{"event": "test", "user_id": 123}'
# For production webhooks, use the "Production URL" (always active)
# Test URL only works when workflow editor is open
Step 7: Environment Variables for Credentials
<code"># Store sensitive values in environment variables instead of n8n's credential store
# docker-compose.yml environment:
- N8N_EXTERNAL_HOOK_FILES=/home/node/.n8n/hooks.js
# Reference in workflow expressions:
# {{ $env.OPENAI_API_KEY }}
# {{ $env.STRIPE_SECRET }}
# Add to .env file:
echo "OPENAI_API_KEY=sk-..." >> .env
echo "STRIPE_SECRET=sk_live_..." >> .env
Getting Started
n8n needs 500 MB–1 GB RAM for the server and PostgreSQL combined. A 2 GB Ubuntu VPS at VPS.DO runs n8n with room for moderate workflow volumes. For high-throughput automations (thousands of executions per day), scale to 4 GB and consider a separate PostgreSQL instance. The EXECUTIONS_DATA_SAVE_ON_SUCCESS=none setting significantly reduces disk and database usage for high-volume workflows.
Conclusion
Self-hosted n8n provides Zapier-like automation with 400+ integrations, unlimited executions, custom JavaScript code nodes, and private data processing — at VPS cost. For developer teams automating internal workflows, the code node capability separates n8n from purely no-code tools: you can write real JavaScript for complex data transformation, conditional logic, and API calls that don’t have a dedicated n8n node. Sensitive data (customer PII, API keys, internal metrics) never leaves your infrastructure.