AI Automation

Chạy nhiều instance n8n trên cùng VPS với Docker và subdomain riêng

Bạn có một VPS và muốn chạy nhiều instance n8n riêng biệt cho từng dự án, một cho khách hàng, một cho team nội bộ, một cho thử nghiệm? Chạy nhiều instance n8n trên cùng một VPS bằng Docker và subdomain riêng là giải pháp không cần thêm VPS nhưng vẫn đảm bảo cô lập từng môi trường.

  • Kiến trúc: Mỗi instance n8n là một stack Docker Compose riêng, có database PostgreSQL, volume dữ liệu và credential riêng, expose trên một cổng nội bộ, sau đó Nginx reverse proxy với subdomain riêng.
  • Bảo mật: Webhook chỉ hoạt động trên subdomain cụ thể. Có thể giới hạn IP qua Nginx.
  • Không xung đột: Port, volume, network đều tách bạch.

Yêu cầu trước khi bắt đầu

Vì sao nên chạy nhiều instance n8n trên cùng VPS?

Thay vì thuê thêm VPS cho từng dự án nhỏ, bạn có thể tận dụng tài nguyên còn trống trên một VPS đủ mạnh. Mỗi instance có database PostgreSQL riêng, file .env riêng, mật mã credential riêng, không sợ workflow của instance này ảnh hưởng instance kia. Điều này phù hợp cho agency, freelancer, hoặc sysadmin muốn tách staging, production, sandbox trên 1 máy.

Bước 1 - Chuẩn bị cấu trúc thư mục

Tạo thư mục gốc chứa tất cả instance, mỗi instance một thư mục con:

mkdir -p /opt/n8n-instances/team1 /opt/n8n-instances/team2
cd /opt/n8n-instances/team1

Bước 2 - Tạo file docker-compose.yml cho instance đầu tiên

Đây là template chuẩn dùng PostgreSQL làm database và lưu credential ở thư mục cục bộ. Mỗi instance dùng một cổng nội bộ khác nhau (ví dụ: team1 dùng 5678, team2 dùng 5680).

cat <<EOF > /opt/n8n-instances/team1/docker-compose.yml
version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: <CHANGE_ME_TEAM1_DB_PASS>
      POSTGRES_DB: n8n
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
    networks:
      - n8n-net

  n8n:
    image: n8nio/n8n:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: <CHANGE_ME_TEAM1_DB_PASS>
      DB_POSTGRESDB_DATABASE: n8n
      N8N_HOST: team1.n8n.example.com
      N8N_PORT: 5678
      N8N_PROTOCOL: https
      WEBHOOK_URL: https://team1.n8n.example.com
      N8N_ENCRYPTION_KEY: <CHANGE_ME_TEAM1_ENCRYPT_KEY>
      N8N_METRICS: false
    volumes:
      - ./n8n-data:/home/node/.n8n
    depends_on:
      - postgres
    networks:
      - n8n-net

networks:
  n8n-net:
    driver: bridge
EOF

Giải thích:

  • 127.0.0.1:5678:5678, Chỉ bind trên localhost, không expose ra internet. Nginx sẽ proxy qua port này.
  • N8N_HOSTWEBHOOK_URL, Đặt đúng subdomain của instance đó.
  • N8N_ENCRYPTION_KEY, Dùng openssl rand -hex 32 để sinh key riêng cho mỗi instance. Key này cực kỳ quan trọng, nếu mất, bạn không đọc được credential đã lưu.
  • POSTGRES_PASSWORD, Mật khẩu database riêng, không dùng chung giữa các instance.

Bước 3 - Tạo instance thứ hai (team2)

Sao chép và chỉnh sửa các giá trị cho khác biệt:

mkdir -p /opt/n8n-instances/team2
cd /opt/n8n-instances/team2
# Tạo file docker-compose.yml tương tự nhưng sửa:
#   - port: 127.0.0.1:5680:5678
#   - N8N_HOST: team2.n8n.example.com
#   - WEBHOOK_URL: https://team2.n8n.example.com
#   - POSTGRES_PASSWORD: khác team1
#   - N8N_ENCRYPTION_KEY: khác team1

Lặp lại bước này cho mỗi instance bạn cần. Đặt tên thư mục theo subdomain tương ứng để dễ quản lý.

Bước 4 - Khởi chạy các instance

Vào từng thư mục và chạy:

docker compose up -d

Kiểm tra trạng thái:

docker compose ps

Bạn sẽ thấy mỗi stack (team1, team2) có 2 container: postgres và n8n.

Bước 5 - Cấu hình Nginx reverse proxy cho từng subdomain

Tạo file cấu hình Nginx cho mỗi instance. Ví dụ cho team1:

cat <<EOF > /etc/nginx/sites-available/team1.n8n.example.com
server {
    listen 80;
    server_name team1.n8n.example.com;
    return 301 https://\$server_name\$request_uri;
}

server {
    listen 443 ssl http2;
    server_name team1.n8n.example.com;

    ssl_certificate /etc/letsencrypt/live/team1.n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/team1.n8n.example.com/privkey.pem;
    include snippets/ssl-params.conf; # optional

    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-Proto \$scheme;
        proxy_set_header X-Forwarded-Host \$host;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_buffering off;
        proxy_cache_bypass \$http_upgrade;
        proxy_read_timeout 3600;
    }
}
EOF

Tương tự cho team2, chỉ khác server_nameproxy_pass http://127.0.0.1:5680;. Sau đó enable site và kiểm tra:

ln -s /etc/nginx/sites-available/team1.n8n.example.com /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/team2.n8n.example.com /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

Nếu chưa có SSL, dùng Certbot để cấp chứng chỉ cho từng subdomain:

certbot --nginx -d team1.n8n.example.com -d team2.n8n.example.com

Bước 6 - Kiểm tra hoạt động

Truy cập https://team1.n8n.example.com trên trình duyệt. Bạn sẽ thấy màn hình đăng nhập n8n của instance team1. Đăng ký tài khoản admin đầu tiên. Sau đó truy cập https://team2.n8n.example.com, login riêng biệt, credential riêng. Kiểm tra port nội bộ đã khoá:

ss -tlnp | grep 5678
# Output: LISTEN 127.0.0.1:5678 (chỉ localhost, không có 0.0.0.0)

Nếu thấy 0.0.0.0:5678 thì port đang mở ra internet. Bạn cần sửa Docker Compose lại thành 127.0.0.1:5678:5678 và restart.

Câu hỏi thường gặp

Mỗi instance n8n dùng bao nhiêu RAM?

n8n cơ bản cần khoảng 200-400 MB RAM mỗi instance khi idle. PostgreSQL thêm 100-200 MB. Tổng mỗi instance tiêu tốn ~400-600 MB RAM. Với VPS 4 GB bạn có thể chạy 4-5 instance ổn định.

Có thể dùng chung một PostgreSQL cho nhiều instance không?

Được, nhưng không khuyến nghị. Mỗi instance cần database riêng trên cùng một server PostgreSQL. Bạn phải set POSTGRES_DB khác nhau (vd n8n_team1, n8n_team2) và ports chỉ expose 1 instance PostgreSQL. Nhưng cô lập kém hơn, dễ xung đột khi migrate.

N8N_ENCRYPTION_KEY có cần phải khác nhau không?

Có. Key này dùng để mã hóa credential của instance đó. Nếu dùng chung key, về mặt lý thuyết ai có quyền truy cập file n8n-data của instance này có thể giải mã credential của instance kia. Luôn sinh key riêng bằng openssl rand -hex 32.

Làm sao để cập nhật n8n cho từng instance?

Vào thư mục instance đó, chạy docker compose pull n8n && docker compose up -d. Dừng từng instance riêng lẻ, không ảnh hưởng instance khác.

Webhook có hoạt động qua subdomain không?

Có. webhook URL sẽ là https://team1.n8n.example.com/webhook/xxx. Nginx reverse proxy chuyển tiếp đến container tương ứng. Nhớ set WEBHOOK_URL đúng trong Docker Compose.

Bài viết liên quan

Lưu ý: Bài viết mang tính tham khảo, tổng hợp kiến thức chung. Mỗi hệ thống, hạ tầng và nhu cầu có đặc thù riêng, nên kiểm thử trong môi trường an toàn và tham vấn kỹ sư trước khi triển khai thực tế.