Self-Hosted Workflow Automation vs Cloud SaaS Platforms
What You’ll Need
- n8n Cloud or self-hosted n8n
- Hetzner VPS or Contabo VPS for self-hosting
- Namecheap if you need a domain
- DigitalOcean as an alternative hosting option
Table of Contents
- The Core Difference: Where Your Data Actually Lives
- Self-Hosted Automation: Full Control, Full Responsibility
- Cloud SaaS Platforms: Simplicity With Trade-offs
- Cost Breakdown: What You Really Pay
- Security and Data Privacy: The Real Conversation
- Performance and Scaling: Where Each Model Breaks
- Getting Started
The Core Difference: Where Your Data Actually Lives
I’ve been automating workflows for three years now, and the first question I ask clients isn’t “What do you want to automate?” It’s “Where does your data need to live?”
That single question determines everything.
Self-hosted automation means your workflows run on your servers. Cloud SaaS means a vendor manages the infrastructure. It sounds simple, but this distinction ripples through cost, security, performance, and control in ways most people don’t discover until they’ve already committed.
I’m not going to pretend one is universally better. I’ve built production systems on both. What I will do is show you exactly what you’re trading off, with real numbers and working code, so you can make an informed decision based on your actual constraints—not marketing copy.
Self-Hosted Automation: Full Control, Full Responsibility
Self-hosting means running something like n8n on your own VPS. I’ve done this extensively. Here’s what you actually get.
What Self-Hosting Gives You
Complete data control. Your workflow data, logs, and execution history never leave your infrastructure. If you process customer PII, healthcare data, or anything compliance-sensitive, this matters enormously.
Zero usage-based costs. You pay for the server, not per workflow execution or API call. I wrote a detailed guide on how I run 3 automated systems on a single $7/month VPS , and that’s the real economics here.
Customization without limits. You can modify the source code, add custom nodes, integrate with internal systems, use private webhooks—whatever you need.
No vendor lock-in. Your workflows are portable. If your host changes pricing or shuts down, you migrate to another server.
The Honest Trade-offs
You handle infrastructure. Need SSL certificates? You set them up. Server crashes at 3 AM? You fix it. Database backups? Your responsibility. This isn’t theoretical—downtime directly impacts your automation.
Scaling isn’t automatic. Adding capacity means manually provisioning more resources or upgrading your plan.
Security is on you. Patches, updates, firewall rules, access controls—you’re the operator. One misconfigured rule can expose your entire setup.
Setting Up Self-Hosted n8n: A Working Example
Let me show you a practical setup using Docker on a Hetzner VPS . This assumes a clean Ubuntu 22.04 instance.
#!/bin/bash
# Update system packages
apt-get update
apt-get upgrade -y
apt-get install -y docker.io docker-compose curl git
# Create directories for persistent storage
mkdir -p /opt/n8n/data
mkdir -p /opt/n8n/logs
# Create docker-compose.yml
cat > /opt/n8n/docker-compose.yml << 'EOF'
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: n8n-postgres
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: your_secure_password_here_change_this
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n_network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: n8nio/n8n:latest
container_name: n8n
ports:
- "5678:5678"
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: your_secure_password_here_change_this
N8N_HOST: your-domain.com
N8N_PORT: 443
N8N_PROTOCOL: https
NODE_ENV: production
WEBHOOK_TUNNEL_URL: https://your-domain.com/
GENERIC_TIMEZONE: UTC
volumes:
- /opt/n8n/data:/home/node/.n8n
- /opt/n8n/logs:/home/node/.n8n/logs
depends_on:
postgres:
condition: service_healthy
networks:
- n8n_network
restart: unless-stopped
nginx:
image: nginx:alpine
container_name: n8n-nginx
ports:
- "80:80"
- "443:443"
volumes:
- /opt/n8n/nginx.conf:/etc/nginx/nginx.conf:ro
- /opt/n8n/ssl:/etc/nginx/ssl:ro
depends_on:
- n8n
networks:
- n8n_network
restart: unless-stopped
volumes:
postgres_data:
networks:
n8n_network:
driver: bridge
EOF
# Create NGINX configuration
cat > /opt/n8n/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;
upstream n8n_backend {
server n8n:5678;
}
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
client_max_body_size 50M;
location / {
proxy_pass http://n8n_backend;
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_redirect off;
proxy_buffering off;
}
}
}
EOF
# Generate self-signed SSL certificate (replace with Let's Encrypt later)
mkdir -p /opt/n8n/ssl
openssl req -x509 -newkey rsa:4096 -nodes -out /opt/n8n/ssl/cert.pem -keyout /opt/n8n/ssl/key.pem -days 365 -subj "/C=US/ST=State/L=City/O=Organization/CN=your-domain.com"
# Start services
cd /opt/n8n
docker-compose up -d
# Wait for services to start
sleep 10
# Check status
docker-compose ps
echo "N8N is starting. Access at https://your-domain.com (accept self-signed cert for now)"
echo "Replace self-signed certificate with Let's Encrypt when ready"
After this setup, you have a fully functional n8n instance with PostgreSQL persistence, NGINX reverse proxy, and SSL. Replace the placeholder passwords and domain name before running.
💡 Fast-Track Your Project: Don’t want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-HUGO.
Building a Self-Hosted Workflow: Complete Example
Let me show you a practical self-hosted workflow. This example pulls data from an API, processes it, and sends it to multiple destinations. Since this runs on your server, you control every execution step.
{
"nodes": [
{
"parameters": {
"url": "https://api.example.com/data",
"authentication": "genericCredentialType",
"genericCredentials": "api_key_credential",
"requestMethod": "GET"
},
"name": "Fetch Data",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [250, 300]
},
{
"parameters": {
"operation": "filter",
"filterType": "string",
"conditions": {
"conditions": [
{
"keyName": "status",
"condition": "equals",
"value": "active"
}
]
}
},
"name": "Filter Active Records",
"type": "n8n-nodes-base.filter",
"typeVersion": 1,
"position": [450, 300]
},
{
"parameters": {
"fieldToSplitOut": "items",
"include": "selectedFields",
"selectedFields": {
"item": {
"id": {},
"name": {},
"value": {}
}
}
},
"name": "Split Data",
"type": "n8n-nodes-base.splitInBatches",
"typeVersion": 3,
"position": [650, 300]
},
{
"parameters": {
"resource": "message",
"operation": "create",
"chatId": "your_channel_id",
"text": "=Record: {{$node[\"Split Data\"].json.id}} - {{$node[\"Split Data\"].json.name}} - Value:
Want to automate this yourself?
Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.
📬 Get Weekly Automation Tips
One email per week with tutorials, tools, and workflows. No spam, unsubscribe anytime.