AI-ready skill doc: bạn đang vibe code với Claude Code/Cursor/Codex/Gemini? Copy nguyên bài này paste vào AI của bạn - nó sẽ đọc như một skill document và áp dụng đúng pattern này vào project mà không cần bạn giải thích lại. Tiện, gọn, dùng được luôn.
n8n cloud bản trả phí khoảng 20 USD/tháng nhưng giới hạn execution. Self-host n8n trên VPS Việt Nam chỉ ~600-1000k/tháng, không giới hạn workflow, dữ liệu tự lưu, integrate được internal tool nội bộ. Bài này hướng dẫn full setup n8n trên Ubuntu 24.04 với Docker Compose, PostgreSQL backend, Caddy auto HTTPS, daily backup, và secure env vars - chuẩn production cho dev solo và team nhỏ.
Tại sao self-host n8n trên VPS?
Năm 2026 n8n đã trở thành tool automation hàng đầu cho dev Việt - thay thế Zapier/Make với chi phí thấp hơn và full control. Lý do self-host:
- Unlimited workflow execution: bản cloud giới hạn execution/tháng theo plan, self-host không giới hạn.
- Data ở VPS của bạn: credential API key, webhook payload, customer data đều tự lưu - không qua server bên thứ ba.
- Custom node + community node: install bất cứ community node nào, viết custom code node tự do.
- Integrate hệ thống nội bộ: n8n trên VPS có thể gọi internal API (database công ty, intranet) qua private network mà n8n cloud không làm được.
- Chi phí ổn định: 600-1000k/tháng cho VPS vs 240 USD/năm n8n cloud, lại không lo bị tăng giá.
Cấu hình VPS phù hợp chạy n8n
| Use case | vCPU | RAM | Disk | Gói tham khảo |
|---|---|---|---|---|
| Cá nhân, <20 workflow, <100 execution/ngày | 2 | 2GB | 30GB | VPS 30 (299k) |
| Team nhỏ, 20-50 workflow, 1k execution/ngày | 4 | 4GB | 50GB | VPS 50 (639k) |
| Agency/team, 50+ workflow, 10k execution/ngày | 6 | 6GB | 80GB | VPS 80 (999k) |
| Production, workflow nặng (LLM, AI) | 8 | 8GB | 160GB | VPS 160 (1.790.000đ) |
Mẹo: n8n + PostgreSQL + Caddy cần ít nhất 2GB RAM idle. VPS 1GB không chạy nổi setup này. Nếu thấy n8n bị OOM kill, đó là dấu hiệu cần upgrade RAM.
Step 1: Đặt VPS Ubuntu 24.04 + cấu hình ban đầu
Đặt VPS với OS Ubuntu 24.04 LTS. Sau khi nhận IP và root password qua email, SSH vào:
# Từ máy bạnssh root@<IP_VPS> # Update hệ thốngapt update && apt upgrade -y # Tạo user non-root (đừng dùng root cho daily work)adduser n8nadminusermod -aG sudo n8nadmin # Copy SSH key sang user mới (để login không cần password)rsync --archive --chown=n8nadmin:n8nadmin ~/.ssh /home/n8nadmin # Logout, login lại với user mớiexitssh n8nadmin@<IP_VPS>Step 2: Cài Docker + Docker Compose
# Cài Docker officialcurl -fsSL https://get.docker.com -o get-docker.shsudo sh get-docker.sh # Cho user dùng docker không cần sudosudo usermod -aG docker $USER # Logout/login lại để áp dụng groupexitssh n8nadmin@<IP_VPS> # Verifydocker --versiondocker compose versionDocker Compose hiện đại đã tích hợp vào Docker CLI (lệnh là , không phải kiểu cũ).
Step 3: Setup firewall (UFW)
sudo ufw default deny incomingsudo ufw default allow outgoingsudo ufw allow sshsudo ufw allow 80/tcpsudo ufw allow 443/tcpsudo ufw enablesudo ufw statusChỉ mở port 22 (SSH), 80 (HTTP, cho Let's Encrypt verify), 443 (HTTPS). PostgreSQL và n8n internal không mở public.
Step 4: Trỏ domain về VPS
Vào DNS provider (Cloudflare, namesilo, godaddy), tạo A record:
Type: AName: n8n (hoặc subdomain bạn muốn)Value: <IP_VPS>TTL: Auto / 300Proxy status: DNS only (TẮT proxy Cloudflare lúc đầu để Let's Encrypt verify được)Test DNS đã propagate:
dig n8n.yourdomain.com +short# Phải trả ra IP VPSStep 5: Tạo cấu trúc thư mục và file .env
mkdir -p ~/n8n-stack/{caddy,n8n,postgres,backups}cd ~/n8n-stack # Tạo file .envcat > .env <<'EOF'# DomainDOMAIN=n8n.yourdomain.com[email protected] # n8nN8N_ENCRYPTION_KEY=<random_32_char_string>N8N_USER_MANAGEMENT_JWT_SECRET=<random_32_char_string> # PostgreSQLPOSTGRES_USER=n8nPOSTGRES_PASSWORD=<strong_random_password>POSTGRES_DB=n8nEOF # Set permission file env (CHỈ owner đọc được)chmod 600 .envGenerate random secret:
# Trên VPSopenssl rand -hex 32# Copy output, paste vào .env cho N8N_ENCRYPTION_KEY và JWT_SECRET (mỗi cái 1 string riêng) # Generate password Postgresopenssl rand -base64 24 Lưu ý: N8N_ENCRYPTION_KEY dùng để mã hóa credential trong n8n. Nếu MẤT key này, mọi credential trong workflow bị mất (phải nhập lại). LƯU key này vào password manager (1Password, Bitwarden) ngay khi tạo.
Step 6: Viết docker-compose.yml
cat > docker-compose.yml <<'EOF'services: caddy: image: caddy:2-alpine container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./caddy/Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config networks: - n8n-net environment: - DOMAIN=${DOMAIN} - ACME_EMAIL=${ACME_EMAIL} postgres: image: postgres:16 container_name: n8n-postgres restart: unless-stopped environment: - POSTGRES_USER=${POSTGRES_USER} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_DB=${POSTGRES_DB} volumes: - postgres_data:/var/lib/postgresql/data networks: - n8n-net healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] interval: 10s timeout: 5s retries: 5 n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped depends_on: postgres: condition: service_healthy environment: - 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} - N8N_HOST=${DOMAIN} - N8N_PORT=5678 - N8N_PROTOCOL=https - WEBHOOK_URL=https://${DOMAIN}/ - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} - N8N_USER_MANAGEMENT_JWT_SECRET=${N8N_USER_MANAGEMENT_JWT_SECRET} - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh - TZ=Asia/Ho_Chi_Minh volumes: - n8n_data:/home/node/.n8n networks: - n8n-net volumes: caddy_data: caddy_config: postgres_data: n8n_data: networks: n8n-net: driver: bridgeEOFStep 7: Viết Caddyfile (auto HTTPS)
cat > caddy/Caddyfile <<'EOF'{$DOMAIN} { tls {$ACME_EMAIL} reverse_proxy n8n:5678 { flush_interval -1 header_up Host {host} header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} header_up X-Forwarded-Proto {scheme} } encode gzip header { Strict-Transport-Security "max-age=31536000;" X-Content-Type-Options "nosniff" X-Frame-Options "SAMEORIGIN" Referrer-Policy "strict-origin-when-cross-origin" }}EOFLưu ý quan trọng: flush_interval -1 cần cho n8n vì workflow lâu chạy (long-running), không có flush này thì connection bị buffer và đứt giữa chừng.
Step 8: Khởi động stack
cd ~/n8n-stackdocker compose up -d # Theo dõi logdocker compose logs -f # Stop log: Ctrl+CLần đầu Caddy sẽ request certificate từ Let's Encrypt - mất 10-30 giây. Khi log Caddy hiện "certificate obtained successfully", truy cập https://n8n.yourdomain.com.
Trang setup n8n hiện ra - tạo owner account đầu tiên (email + password). Đây là admin của instance.
Step 9: Daily backup database tự động
Backup quan trọng vì mất DB = mất tất cả workflow. Tạo script backup:
cat > ~/n8n-stack/backup.sh <<'EOF'#!/bin/bashset -e BACKUP_DIR=~/n8n-stack/backupsDATE=$(date +%Y%m%d_%H%M%S)KEEP_DAYS=14 cd ~/n8n-stack # Dump databasedocker compose exec -T postgres pg_dump -U n8n n8n | gzip > $BACKUP_DIR/n8n_db_${DATE}.sql.gz # Backup volume n8n_data (chứa credential encryption seed)docker run --rm -v n8n-stack_n8n_data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/n8n_data_${DATE}.tar.gz -C /data . # Xóa backup cũ hơn KEEP_DAYS ngàyfind $BACKUP_DIR -name "*.gz" -mtime +$KEEP_DAYS -delete echo "Backup completed: $DATE"EOF chmod +x ~/n8n-stack/backup.shSchedule cron chạy 3h sáng mỗi ngày:
crontab -e # Thêm dòng:0 3 * * * /home/n8nadmin/n8n-stack/backup.sh >> /home/n8nadmin/n8n-stack/backup.log 2>&1Test chạy thử backup:
./backup.shls -lh backups/Step 10: Upload backup ra cloud storage (S3/R2)
Backup local không đủ - VPS chết là mất hết. Upload lên cloud storage:
# Cài AWS CLIsudo apt install awscli -y # Cấu hình credential cho Cloudflare R2 (rẻ + miễn phí egress)aws configure --profile r2# AWS Access Key ID: <R2_access_key># AWS Secret Access Key: <R2_secret_key># Default region: auto# Default output: json # Sửa backup.sh thêm phần uploadcat >> ~/n8n-stack/backup.sh <<'EOF' # Upload to Cloudflare R2aws s3 cp $BACKUP_DIR/n8n_db_${DATE}.sql.gz s3://your-bucket/n8n-backups/ --endpoint-url https://<account_id>.r2.cloudflarestorage.com --profile r2 aws s3 cp $BACKUP_DIR/n8n_data_${DATE}.tar.gz s3://your-bucket/n8n-backups/ --endpoint-url https://<account_id>.r2.cloudflarestorage.com --profile r2EOFStep 11: Update n8n định kỳ (an toàn)
n8n release version mới đều đặn. Update:
cd ~/n8n-stack # Backup TRƯỚC khi update./backup.sh # Pull image mớidocker compose pull # Recreate container (giữ volume data)docker compose up -d # Verifydocker compose logs n8n --tail 50Nếu update gây lỗi (rare), rollback bằng cách pin version cụ thể trong docker-compose.yml:
# Thay vìimage: n8nio/n8n:latest# Pin cụ thể version đang chạy stableimage: n8nio/n8n:1.65.2 # ví dụStep 12: Bảo mật bổ sung
SSH key only, disable password
sudo nano /etc/ssh/sshd_config# Sửa: PasswordAuthentication no# Sửa: PermitRootLogin nosudo systemctl restart sshFail2ban chống brute force
sudo apt install fail2ban -ysudo systemctl enable fail2banAuto security update
sudo apt install unattended-upgrades -ysudo dpkg-reconfigure -plow unattended-upgradesWebhook secret cho từng workflow
Trong n8n, khi tạo Webhook node, set "Authentication" = "Header Auth" hoặc check token trong workflow logic. Không để webhook public không xác thực - bot scan internet liên tục để abuse webhook.
Monitoring n8n: biết khi nào workflow fail
Built-in n8n có "Execution List" - xem history tất cả execution. Nhưng cần alert chủ động khi workflow fail.
Cách 1: Tạo Error Workflow trong n8n - trigger khi bất kỳ workflow nào fail, gửi notification về Telegram/Slack/Email. Workflow:
- Tạo workflow mới tên "Error Handler".
- Node 1: Error Trigger (auto trigger khi workflow khác fail).
- Node 2: Telegram Send Message với chi tiết error.
- Vào Settings từng workflow quan trọng, set "Error Workflow" = "Error Handler".
Cách 2: External monitoring qua UptimeRobot - ping HTTPS healthcheck endpoint, alert qua email/Telegram khi n8n down.
Troubleshooting thường gặp
n8n container restart liên tục, log "OOM killed"
RAM không đủ. Upgrade VPS hoặc giảm worker. Check: xem container nào ngốn RAM.
Webhook không nhận được request từ ngoài
Check firewall UFW có open 443. Check Cloudflare proxy (orange cloud) - n8n cần WebSocket hỗ trợ, một số rule Cloudflare có thể block. Test: .
Caddy không lấy được cert HTTPS
DNS chưa propagate đúng IP, hoặc port 80 bị block. Test - phải trả 308 redirect tới HTTPS.
Workflow chạy chậm hoặc timeout
Tăng timeout trong workflow setting. Cân nhắc tách workflow nặng sang queue mode (n8n có doc về queue mode với BullMQ Redis).
Use case n8n self-host phổ biến cho dev Việt
- Sync data giữa Google Sheet và database SaaS: mỗi khi sales nhập lead vào sheet, n8n tự push sang CRM.
- Auto-reply Zalo OA/Facebook: webhook nhận message, gọi OpenAI/Claude, gửi response.
- Báo cáo daily Slack/Telegram: mỗi 8h sáng, n8n query database, gửi report doanh số đêm qua.
- Backup tự động cross-platform: dump database mỗi đêm, upload R2, gửi notification.
- RPA cho ads: mỗi giờ check Google Ads/Meta Ads spend, alert khi vượt budget.
- Workflow AI: input từ form → OpenAI process → save Notion/Airtable.
Tổng kết: self-host n8n trên VPS giúp dev/team kiểm soát hoàn toàn workflow automation với chi phí thấp hơn nhiều bản cloud. Stack đề xuất: VPS Việt Nam giá tốt + Ubuntu 24.04 + Docker Compose + PostgreSQL + Caddy auto HTTPS + cron backup hàng ngày + upload R2 + monitoring qua Error Workflow. Setup ban đầu khoảng 1-2 giờ, sau đó chạy ổn định nhiều tháng không cần can thiệp. Bắt đầu với VPS 50 cho cá nhân, scale lên VPS 80/160 khi workflow tăng.
