I was spending 20–30 minutes per week on the same repetitive task: copying data from one system to another, sending formatted summaries to a Slack channel, moving files between services. Small things individually, but they added up.
n8n automates all of that. It's a visual workflow builder where you connect nodes — Slack, Airtable, email, webhooks, HTTP requests, whatever you need — and define the logic between them. I set up one workflow in an afternoon that now saves me that weekly maintenance time.
Self-hosting n8n means no per-step pricing, no workflow limits, and full control over the automation logic and data. The one thing to understand upfront: webhooks require HTTPS, so domain setup is part of the installation.
This guide deploys n8n on Ubuntu 22.04 with Docker Compose, PostgreSQL for persistence, Nginx as the reverse proxy, and HTTPS.
I run n8n on Tencent Cloud Lighthouse. The 2 vCPU / 4 GB RAM plan runs a production n8n instance comfortably. Use Lighthouse's Docker CE application image when creating your instance — Docker is pre-installed and ready, so you skip the Docker setup step entirely. n8n's cloud pricing is per-execution, which adds up for high-frequency automations. Self-hosting on Lighthouse gives you unlimited workflow executions at a flat monthly cost — predictable pricing regardless of how many times your automations run.
- Key Takeaways
n8n connects your tools and automates workflows. Some examples:
| Trigger | Action |
|---|---|
| New GitHub issue | Post to Slack channel |
| Form submission | Add row to Google Sheets + send email |
| RSS feed update | Save to Notion database |
| Webhook from Stripe | Create invoice in accounting system |
| Scheduled daily | Generate report + email to team |
| New tweet mentioning you | Log to database |
n8n has 400+ built-in integrations including:
Slack, Discord, Telegram, GitHub, GitLab, Google Sheets, Notion, Airtable, MySQL, PostgreSQL, MongoDB, Redis, AWS S3, Cloudflare, Stripe, Twilio, SendGrid, Mailchimp, HubSpot, and many more.
| Requirement | Notes |
|---|---|
| Cloud server | Tencent Cloud Lighthouse Ubuntu 22.04 |
| Docker + Compose | Installed |
| Domain name | Required for webhooks to work |
ssh ubuntu@YOUR_SERVER_IP
sudo apt update && sudo apt upgrade -y
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
sudo apt install -y nginx
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enable
mkdir -p ~/apps/n8n && cd ~/apps/n8n
Create .env:
nano .env
# PostgreSQL database
POSTGRES_USER=n8n
POSTGRES_PASSWORD=generate_strong_password
POSTGRES_DB=n8n
POSTGRES_HOST=n8n-db
# n8n settings
N8N_HOST=n8n.yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com/
N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com/
# Timezone
GENERIC_TIMEZONE=America/New_York
# Encryption key for credentials (generate once, never change)
N8N_ENCRYPTION_KEY=generate_a_long_random_string_here
# Basic auth for the n8n UI (optional but recommended)
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=choose_strong_password
# Execution data retention
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168 # Keep last 7 days
chmod 600 .env
Create docker-compose.yml:
version: '3.8'
services:
n8n-db:
image: postgres:16-alpine
container_name: n8n-db
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- n8n_db:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
n8n:
image: docker.n8n.io/n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=${POSTGRES_HOST}
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_HOST=${N8N_HOST}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_EDITOR_BASE_URL=${N8N_EDITOR_BASE_URL}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE}
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
- EXECUTIONS_DATA_PRUNE=${EXECUTIONS_DATA_PRUNE}
- EXECUTIONS_DATA_MAX_AGE=${EXECUTIONS_DATA_MAX_AGE}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
n8n-db:
condition: service_healthy
volumes:
n8n_db:
n8n_data:
Start n8n:
docker compose up -d
docker compose logs -f n8n
# Wait for: Editor is now accessible via...
sudo nano /etc/nginx/sites-available/n8n
server {
listen 80;
server_name n8n.yourdomain.com;
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
# WebSocket support (required for n8n)
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;
proxy_read_timeout 300s;
}
}
sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d n8n.yourdomain.com
Visit https://n8n.yourdomain.com.
| Category | Examples |
|---|---|
| Triggers | Schedule, Webhook, Email trigger, RSS, Database change |
| Actions | Send email, Post to Slack, Create DB row, HTTP Request |
| Logic | IF, Switch, Merge, Split, Loop |
| Data | JSON transform, Code (JS), Set/Get variable |
| Services | GitHub, Notion, Airtable, Google Sheets, Stripe |
n8n can receive webhooks from any external service that supports them.
Add a Webhook node:
/my-webhookYour webhook URL: https://n8n.yourdomain.com/webhook/my-webhook
Configure a service to call this URL (e.g., GitHub, Stripe, a custom script):
# Test your webhook
curl -X POST https://n8n.yourdomain.com/webhook/my-webhook \
-H "Content-Type: application/json" \
-d '{"event": "test", "data": {"message": "Hello n8n"}}'
The workflow executes when the webhook receives a request.
nano ~/backup_n8n.sh
#!/bin/bash
BACKUP_DIR=~/backups/n8n
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
source ~/apps/n8n/.env
# Export all workflows as JSON
docker exec n8n n8n export:workflow --all --output=/home/node/.n8n/backup_workflows_$DATE.json 2>/dev/null || true
# Backup PostgreSQL database
docker exec n8n-db pg_dump -U n8n n8n | \
gzip > $BACKUP_DIR/n8n_db_$DATE.sql.gz
# Backup n8n data volume (includes credentials and config)
docker run --rm \
-v n8n_n8n_data:/data \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/n8n_data_$DATE.tar.gz -C /data .
find $BACKUP_DIR -mtime +7 -delete
echo "n8n backup complete: $DATE"
chmod +x ~/backup_n8n.sh
(crontab -l; echo "0 2 * * * ~/backup_n8n.sh") | crontab -
n8n webhooks must be accessible via HTTPS for most external services (GitHub, Stripe, Slack, etc.) to send payloads to them. HTTP webhook URLs are rejected by most services.
If webhooks aren't being received:
curl -I https://n8n.yourdomain.com/webhook/testWEBHOOK_URL in .env uses https://docker compose logs -f n8n | grep webhookAlso ensure the Lighthouse console firewall allows HTTPS traffic on port 443 — webhooks come from external IPs.
Developer workflows:
Content workflows:
Business workflows:
Personal workflows:
| Issue | Likely Cause | Fix |
|---|---|---|
| Connection refused | Service not running or wrong port | Check systemctl status SERVICE and verify firewall rules |
| Permission denied | Wrong file ownership or permissions | Check file ownership with ls -la and use chown/chmod to fix |
| 502 Bad Gateway | Backend service not running | Restart the backend service; check logs with journalctl -u SERVICE |
| SSL certificate error | Certificate expired or domain mismatch | Run sudo certbot renew and verify domain DNS points to server IP |
| Service not starting | Config error or missing dependency | Check logs with journalctl -u SERVICE -n 50 for specific error |
| Out of disk space | Logs or data accumulation | Run df -h to identify usage; clean logs or attach CBS storage |
| High memory usage | Too many processes or memory leak | Check with htop; consider upgrading instance plan if consistently high |
| Firewall blocking traffic | Port not open in UFW or Lighthouse console | Open port in Lighthouse console firewall AND sudo ufw allow PORT |
What's the difference between n8n and built-in Linux cron?
Linux cron runs commands on a schedule. n8n provides additional capabilities like visual management, dependency handling, error notifications, and often Docker-native integration.
How do I debug a failing n8n task?
Check the execution logs first. Verify the command works when run manually with the same user/environment. Common issues: incorrect paths, missing environment variables, permission problems.
How do I make n8n tasks resilient to failures?
Implement retry logic, alert on failures (email/Slack notification), and log output to a file. For critical tasks, consider writing a simple success/failure status to a monitoring endpoint.
What happens if the server restarts — do scheduled tasks continue?
If configured as a systemd service (as shown in this guide), n8n restarts automatically on server reboot and resumes its schedule. Configure Restart=on-failure for crash recovery.
systemctl or its own web interface. For important tasks, write output to a log file and use a monitoring tool to check for failures.Automate your workflows today:
👉 Tencent Cloud Lighthouse — Reliable VPS for n8n
👉 View current pricing and promotions
👉 Explore all active deals and offers