n8n is an open-source workflow automation platform built on the "if this happens, do that" principle. Data scraping, notifications, service synchronization, webhook processing — everything's built visually without coding. Think Zapier or Make, but without monthly bills and with full control over your data.
The cloud version n8n.cloud charges $20-50/month for convenience. Self-hosted on your own server costs €5-15/month for a VPS — the savings are obvious if you plan long-term use. Plus no limits on operations, data privacy, and complete customization freedom.
This guide shows how to deploy n8n on your server in 15 minutes from scratch. SSL certificate, domain name, automatic restart on reboot, security setup — all included. We'll finish with three practical automation scenarios that work immediately after installation.
What You'll Need
Linux Server:
Minimum 1 vCPU / 2 GB RAM / 20 GB disk. This handles 5-10 concurrent workflows comfortably. For active usage, grab 2 vCPU / 4 GB RAM for headroom.
Operating system: Ubuntu 22.04 or 24.04. Instructions written for Ubuntu, but Debian works identically.
Domain Name:
You need a domain or subdomain to access n8n via a clean address like automation.your-domain.com. Register one for €10/year or use an existing domain.
DNS A record should point to your server's IP address. Configure this in your domain panel — add a record automation with value 123.45.67.89 (your IP).
Email for SSL:
Let's Encrypt requires an email when issuing certificates. Used for renewal notifications — any working address works.
SSH Access:
Connection to server via SSH with sudo privileges. If you just created the VPS, credentials usually arrive via email — root login and password or SSH key.
Server Preparation
Connect to your server:
ssh root@123.45.67.89
Update system packages:
apt update && apt upgrade -y
Install Docker — the engine for running containers. n8n will run inside an isolated container, which is cleaner than direct system installation:
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Verify Docker installed:
docker --version
Should show something like Docker version 25.0.3, build 4debf41. Version may differ — main thing is the command works without errors.
Enable Docker auto-start on server boot:
systemctl enable docker
systemctl start docker
Deploy n8n via Docker Compose
Create directory where n8n lives with all its dаta:
mkdir -p /opt/n8n
cd /opt/n8n
Create docker-compose.yml file describing how to run n8n:
nano docker-compose.yml
Paste configuration (replace automation.your-domain.com with your actual domain, your@email.com with your email):
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=automation.your-domain.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- WEBHOOK_URL=https://automation.your-domain.com/
- GENERIC_TIMEZONE=America/New_York
- N8N_EMAIL_MODE=smtp
- N8N_SMTP_HOST=smtp.gmail.com
- N8N_SMTP_PORT=465
- N8N_SMTP_USER=your@email.com
- N8N_SMTP_PASS=your-app-password
- N8N_SMTP_SENDER=your@email.com
- N8N_SMTP_SSL=true
volumes:
- /opt/n8n/dаta:/home/node/.n8n
- /opt/n8n/files:/files
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`automation.your-domain.com`)"
- "traefik.http.routers.n8n.entrypoints=websecure"
- "traefik.http.routers.n8n.tls.certresolver=mytlschallenge"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=your@email.com"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /opt/n8n/letsencrypt:/letsencrypt
Save file: Ctrl+O, Enter, Ctrl+X.
What's happening: we launch two containers — n8n itself and Traefik (reverse proxy that automatically gets SSL certificate from Let's Encrypt). n8n will be accessible via HTTPS with automatic HTTP redirect.
Important variables:
N8N_HOST— your domainWEBHOOK_URL— address for webhooks (same domain with https://)GENERIC_TIMEZONE— timezone, change to yours- SMTP settings — for sending notifications (optional, remove if unneeded)
Create data directories:
mkdir -p /opt/n8n/data /opt/n8n/files /opt/n8n/letsencrypt
Launch:
docker compose up -d
Flag -d means background mode. Docker downloads images (takes 1-2 minutes on first run), starts containers, Traefik automatically gets SSL certificate.
Verify everything works:
docker compose ps
Should see two containers with Up status:
NAME IMAGE STATUS
n8n n8nio/n8n:latest Up 30 seconds
traefik traefik:v2.10 Up 30 seconds
Check logs to ensure no errors:
docker compose logs -f n8n
If you see line Editor is now accessible via: https://automation.your-domain.com — you're golden. Press Ctrl+C to exit log view.
First-Time n8n Setup
Open browser and navigate to https://automation.your-domain.com
On first visit, n8n requests owner account creation:
- Email — your email for login
- Password — make it strong, this is access to all automations
After registration, you land in n8n interface. Left sidebar shows workflows (empty for now), right side is the canvas for building automations.
Basic Security:
n8n is accessible to anyone who knows the address by default. For extra protection — set up basic authentication at Traefik level or limit access by IP via firewall.
To limit by IP (for example, only from your office):
ufw allow from 123.45.67.89 to any port 443
ufw allow from 98.76.54.32 to any port 443
ufw enable
Replace IP addresses with yours. Verify you won't lock yourself out before enabling firewall.
Environment Variables Configuration
n8n is controlled through environment variables in docker-compose.yml. Here are important ones to know:
For database work:
By default n8n stores data in SQLite file. For production with heavy loads, PostgreSQL is better:
Add to n8n container's environment section:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=strong-password
And add PostgreSQL service to docker-compose.yml:
postgres:
image: postgres:15
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=strong-password
volumes:
- /opt/n8n/postgres:/var/lib/postgresql/data
After changes, restart:
docker compose down
docker compose up -d
For external API work:
Many workflows call third-party service APIs. Store API keys in environment variables, not hardcoded in workflows:
- TELEGRAM_BOT_TOKEN=your-bot-token
- OPENAI_API_KEY=your-openai-key
- NOTION_API_KEY=your-notion-key
In workflows, reference via $env.TELEGRAM_BOT_TOKEN instead of hardcoding tokens.
For resource limiting:
If n8n starts consuming all memory on complex workflows:
- N8N_PAYLOAD_SIZE_MAX=16
- EXECUTIONS_DATA_MAX_AGE=168
- EXECUTIONS_DATA_PRUNE=true
First line limits data size passed between nodes (16 MB). Second two auto-delete execution history older than a week.
Three Practical Automation Scenarios
Scenario 1: Telegram Bot for Notifications
Task: receive Telegram notification when someone fills out a website form.
Create new bot via @BotFather in Telegram, get token.
In n8n, create new workflow:
-
Webhook node — trigger listening for POST requests
- HTTP Method: POST
- Path:
form-submit - Response Mode: Last Node
-
Telegram node — send message
- Credential: add bot token
- Chat ID: your Telegram ID (find via @userinfobot)
- Text:
New submission!\nName: {{$json.name}}\nEmail: {{$json.email}}
Activate workflow. Get webhook URL like:https://automation.your-domain.com/webhook/form-submit
On website, add form submission to this address:
<form id="contactForm">
<input name="name" required>
<input type="email" name="email" required>
<button type="submit">Submit</button>
</form>
<script>
document.getElementById('contactForm').onsubmit = async (e) => {
e.preventDefault();
const data = new FormData(e.target);
await fetch('https://automation.your-domain.com/webhook/form-submit', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(Object.fromEntries(data))
});
alert('Submitted!');
};
</script>
Now every submission arrives in Telegram instantly.
Scenario 2: Automated Competitor Price Scraping
Task: check competitor's product price daily and log to Google Sheets.
Workflow:
-
Schedule Trigger — run on schedule
- Trigger Times: Cron
0 9 * * *(daily at 9 AM)
- Trigger Times: Cron
-
HTTP Request — scrape page
- Method: GET
- URL:
https://competitor.com/product/12345
-
HTML Extract — extract price
- Selector:
.price(CSS selector for price element) - Extract: Text
- Selector:
-
Set — format data
- Name:
price, Value:{{$json.price.replace(/[^0-9]/g, '')}} - Name:
date, Value:{{$now.format('YYYY-MM-DD')}}
- Name:
-
Google Sheets — write to spreadsheet
- Credential: connect Google account
- Operation: Append
- Sheet ID: your sheet ID
- Range:
A:B
Activate — every day at 9 AM price automatically logs to sheet. After a month you see price trends.
Scenario 3: Server File Backup
Task: weekly archive directory and upload to cloud.
Workflow:
-
Schedule Trigger — every Sunday at 3 AM
- Cron:
0 3 * * 0
- Cron:
-
Execute Command — create archive
- Command:
tar -czf /tmp/backup-{{$now.format('YYYY-MM-DD')}}.tar.gz /var/www/html
- Command:
-
Read Binary File — read created archive
- File Path:
/tmp/backup-{{$now.format('YYYY-MM-DD')}}.tar.gz
- File Path:
-
Dropbox / Google Drive — upload to cloud
- Credential: connect account
- Operation: Upload
- File Name:
backup-{{$now.format('YYYY-MM-DD')}}.tar.gz
-
Execute Command — remove temp file
- Command:
rm /tmp/backup-*.tar.gz
- Command:
Automatic weekly backup without manual intervention.
Maintenance and Backups
n8n Backup:
All n8n data lives in /opt/n8n/data — workflows, credentials, execution history. Copy this directory — save everything.
Simple backup script:
nano /root/backup-n8n.sh
#!/bin/bash
BACKUP_DIR="/root/n8n-backups"
DATE=$(date +%Y-%m-%d-%H%M)
mkdir -p $BACKUP_DIR
tar -czf $BACKUP_DIR/n8n-backup-$DATE.tar.gz /opt/n8n/data
# Remove backups older than 30 days
find $BACKUP_DIR -name "n8n-backup-*.tar.gz" -mtime +30 -delete
Make executable:
chmod +x /root/backup-n8n.sh
Add to crontab for daily run:
crontab -e
Add line:
0 2 * * * /root/backup-n8n.sh
Now every night at 2 AM creates archive with n8n data in /root/n8n-backups.
Updating n8n:
For new features and security fixes, periodically update:
cd /opt/n8n
docker compose pull
docker compose down
docker compose up -d
Command pull downloads latest n8n image version, then restart with new version. All data persists since it's in volumes outside container.
Monitoring Status:
Check status:
docker compose ps
View recent error logs:
docker compose logs --tail=50 n8n
Follow logs in real-time:
docker compose logs -f n8n
Common Issues Solved
n8n won't open in browser:
Verify DNS record configured correctly:
nslookup automation.your-domain.com
Should return your server IP. If not — DNS problem, wait for propagation (up to 24 hours) or check registrar settings.
Check containers are running:
docker compose ps
If status isn't Up — check logs:
docker compose logs traefik
docker compose logs n8n
Webhooks don't trigger:
Ensure WEBHOOK_URL in environment variables is correct — should match N8N_HOST and start with https://.
Verify workflow is activated (Active toggle in upper right corner).
Check webhook path has no leading slash — correct is form-submit, wrong is /form-submit.
Memory errors:
If n8n crashes with jаvascript heap out of memory:
Add to docker-compose.yml in environment section:
- NODE_OPTIONS=--max-old-space-size=2048
Number 2048 = 2 GB RAM for Node.js process. If you have 4 GB on server, can set 3072.
SSL certificate won't issue:
Traefik logs show details:
docker compose logs traefik | grep acme
Common cause — ports 80 or 443 occupied by another process. Check:
ss -tulpn | grep :80
ss -tulpn | grep :443
If nginx or apache is there — stop them before launching n8n.
Another cause — DNS record doesn't point to server. Let's Encrypt verifies domain resolves to IP making the certificate request.
Self-Hosted vs Cloud Version
When to choose self-hosted:
Saves money long-term. Cloud $20-50/month, own VPS €5-15/month. Over a year, difference is €180-420.
Full data control. Everything stored on your server, no third parties access workflows and data flowing through the system.
No execution limits. Cloud basic plan has 5000 executions/month, then extra charges. Self-hosted — unlimited, only limited by server power.
Customization. Can modify code, add custom nodes, integrate with internal services directly.
When to choose cloud:
Don't want to handle administration. Installation, updates, backups, security — all on n8n.cloud shoulders.
Need high availability out of box. Cloud has 99.9% SLA, self-hosted — depends on your setup.
Small workloads. If using couple workflows rarely — paying €15/month for idle VPS may be less attractive than $20/month for cloud with support.
Team collaboration. Cloud makes it easier to set up access for multiple users with different roles.
Conclusion
Self-hosted n8n delivers powerful automation tooling at the cost of a modest VPS. After initial setup, the system runs itself — you just create workflows and get results.
Savings versus cloud version pay back the installation time within 2-3 months. Plus complete freedom — no limits, your data under control, customization for any task.
VPS on THE.Hosting with 2 vCPU / 4 GB RAM / 40 GB NVMe configuration for €10-12/month is perfect for n8n with room to grow. NVMe drives accelerate workflow database operations, and 50+ locations let you choose a server close to your users for minimal webhook latency.
Questions about setup? THE.Hosting support is available 24/7 and helps with server configuration for n8n.