SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor

Self-host n8n trên VPS với Docker và HTTPS

Chạy n8n trên VPS bằng Docker Compose, Postgres và reverse proxy HTTPS. Tránh lỗi WEBHOOK_URL, encryption-key và các error string thường gặp.

Bạn đang xây dựng những gì

n8n là một công cụ tự động hóa quy trình làm việc: một trình chỉnh sửa trực quan nơi các trigger, webhook, lịch trình hoặc biểu mẫu gửi đi sẽ kích hoạt chuỗi các node gọi API, định dạng lại dữ liệu và ghi vào các hệ thống khác. Nó đã trở thành công cụ kết nối mặc định cho các quy trình làm việc của AI-agent vì nó giao tiếp được với mọi nhà cung cấp mô hình và cơ sở dữ liệu mà bạn không cần phải viết code dịch vụ. Một docker run giúp bạn có trình chỉnh sửa hoạt động trong hai phút. Hướng dẫn này tập trung vào chín mươi phần trăm còn lại: làm cho nó bền bỉ hơn với Postgres thay vì file SQLite mặc định, có thể truy cập qua HTTPS và phần mà hầu hết mọi người đều làm sai: đảm bảo các webhook cung cấp một URL mà thế giới bên ngoài thực sự có thể truy cập được.

Stack hoàn chỉnh gồm hai container trên cùng một Docker network: bản thân n8n và cơ sở dữ liệu Postgres lưu trữ các quy trình làm việc và thông tin xác thực của nó. Một reverse proxy trên host thực hiện TLS termination và chuyển tiếp đến n8n qua localhost, vì vậy không có thành phần nào tiếp xúc với lưu lượng truy cập Internet từ bên ngoài ngoại trừ thông qua proxy đó. Nó nằm cùng với các dịch vụ khác trong danh sách rút gọn tự host năm 2026.

Điều kiện tiên quyết và các giới hạn thực tế

Bạn cần một VPS có ít nhất 1 GB RAM; hãy dự trù 2 GB khi workflow bắt đầu xử lý công việc thực tế, vì các execution cùng runtime Node.js sẽ ngốn bộ nhớ, còn việc OOM killer dừng container giữa chừng là cách rất tệ để nhận ra điều đó. Một vCPU là đủ để bắt đầu. Nếu máy này cũng chạy dịch vụ nặng hơn, hãy chọn cấu hình dựa trên dịch vụ đó trước: thư viện ảnh thường là thủ phạm, và mức RAM thực tế tối thiểu cho PhotoPrism và Immich cao hơn nhiều so với yêu cầu của n8n. Media box cũng tương tự: một server Jellyfin cùng giao diện duyệt nội dung cho nó, chẳng hạn như Halcyon, công cụ dựng lại thư viện theo kiểu cửa hàng cho thuê băng đĩa thập niên 90, sẽ chiếm RAM và dư địa transcoding từ lâu trước khi n8n nhận thấy.

Bạn cần một domain hoặc subdomain, chẳng hạn n8n.example.com, có A record trỏ đến public IP của VPS và đã phân giải được trước khi bạn yêu cầu certificate. Cổng 80 và 443 phải được mở cho proxy; cổng riêng của n8n là 5678 không được public ra Internet. Bạn cần Docker Engine và Compose plugin. Nếu docker compose version báo lỗi docker: 'compose' is not a docker command thì bạn đang dùng standalone binary cũ; plugin là sudo apt install docker-compose-plugin.

Dùng SQLite để thử nghiệm là ổn, còn mọi thứ bạn phụ thuộc vào thì dùng Postgres

Database mặc định của n8n là một file SQLite tại /home/node/.n8n/database.sqlite. Để thử nhanh, cách này là đủ; không mount volume thì bạn sẽ mất database ngay lần recreate container đầu tiên, và đó cũng là một bài học riêng. Lý do chuyển sang Postgres không phải vì tốc độ thuần túy, mà vì SQLite chỉ cho phép một writer lock duy nhất. Do đó, một instance chạy nhiều workflow cùng lúc hoặc dùng queue mode mà sau này bạn sẽ cần sẽ phát sinh SQLITE_BUSY: database is locked khi có concurrency. Postgres không bị giới hạn như vậy, hỗ trợ backup rõ ràng bằng pg_dump, và đây là database mà tài liệu chính thức của n8n giả định khi server là thành phần bạn phụ thuộc vào. Chuyển đổi sau này đồng nghĩa với việc phải tự migrate dữ liệu, nên nếu máy này quan trọng, hãy bắt đầu với Postgres.

DNS và firewall

Trỏ bản ghi và mở các port trước để bước cấp chứng chỉ sau đó không fail vì hostname chưa phân giải được.

dig +short n8n.example.com
curl -s ifconfig.me
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw enable

Không mở port 5678. File compose bind n8n vào 127.0.0.1:5678 nên chỉ reverse proxy trên host mới có thể truy cập n8n. Một ufw allow 5678 sẽ phá vỡ cơ chế cô lập đó.

Tệp Compose

Tạo một thư mục làm việc và một docker-compose.yml. Đây là toàn bộ stack: hai service, một network riêng, hai named volume.

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - n8n_net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: docker.n8n.io/n8nio/n8n:2.29.10
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=Europe/London
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - n8n_net
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

networks:
  n8n_net:

Cần nêu rõ một vài quyết định. DB_POSTGRESDB_HOST=postgres là tên service mà Docker phân giải trên network dùng chung, không phải localhost; bên trong container n8n, localhost có nghĩa là chính n8n. depends_on cùng với condition: service_healthy ngăn n8n chạy trước Postgres khi boot; nếu không có chúng, n8n khởi động, không tìm thấy database rồi thoát. Named volume n8n_data được mount tại /home/node/.n8n chứa encryption key và, khi dùng SQLite, cả database; đây là thư mục duy nhất bạn không được làm mất. Ghim image vào một version cụ thể, không bao giờ dùng latest; lý do được nêu trong phần nâng cấp bên dưới.

Tệp secrets

Không bao giờ đặt mật khẩu trong compose file. Hãy đặt chúng trong tệp .env cùng thư mục với compose file để Compose tự động đọc tệp này, đồng thời tạo mật khẩu bằng cách bảo đảm chúng thực sự ngẫu nhiên.

printf 'POSTGRES_PASSWORD=%s\n'  "$(openssl rand -hex 24)" >  .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .env

N8N_ENCRYPTION_KEY là chuỗi quan trọng nhất ở đây. Đây là key dùng để mã hóa mọi credential được lưu trữ. Hãy đặt key này một cách rõ ràng thay vì để n8n tự tạo, vì giá trị do bạn tạo có thể được ghi lại và khôi phục. Sau khi n8n đã mã hóa credential đầu tiên bằng key này, thay đổi key sẽ khiến mọi credential không thể giải mã, vì vậy hãy đặt key một lần ngay bây giờ và không bao giờ sửa lại dòng đó.

Các biến môi trường quyết định webhook có hoạt động hay không

Bốn biến kiểm soát cách n8n tự mô tả với bên ngoài. Cấu hình sai các biến này là câu hỏi được hỗ trợ nhiều nhất về n8n.

  • N8N_HOST là hostname public, n8n.example.com. Nếu giữ giá trị mặc định localhost khi chạy sau proxy, editor sẽ cố tải API của chính nó từ localhost trong trình duyệt của bạn, nên sẽ thất bại.
  • N8N_PROTOCOL=https cho n8n biết dịch vụ được phân phối qua TLS. Vì vậy, n8n đánh dấu session cookie là Secure và tạo các URL https://.
  • N8N_PORT=5678 là cổng n8n lắng nghe bên trong container. Đây không phải public port; proxy quản lý cổng 443.
  • WEBHOOK_URL=https://n8n.example.com/ là biến dễ gây lỗi nhất. n8n tạo các địa chỉ webhook để bạn dán vào Stripe, GitHub hoặc bất kỳ hệ thống gọi bên ngoài nào bằng cách ghép các giá trị này. Nếu biến này chưa được đặt hoặc đặt sai, n8n sẽ dùng N8N_HOST:N8N_PORT và cung cấp cho bạn https://n8n.example.com:5678/webhook/... hoặc tệ hơn là http://localhost:5678/webhook/.... Các URL này được in ra mà không có lỗi, trông có vẻ hợp lệ nhưng không thể truy cập từ Internet, nên request từ hệ thống gọi sẽ âm thầm không bao giờ đến được. Hãy đặt biến này thành public base URL chính xác, có dấu gạch chéo ở cuối. Sau đó xác nhận webhook node hiển thị URL không có port.

N8N_PROXY_HOPS=1 cho Express server của n8n biết có một proxy đứng trước nó. Nhờ đó, rate limiting và mọi tính năng đọc client IP sẽ thấy địa chỉ thật thay vì địa chỉ của proxy. Một biến bạn cố ý không đặt ở đây là N8N_RUNNERS_ENABLED. Task runner là cơ chế để n8n chạy logic của Code node trong một process riêng, tách khỏi process chính. Cơ chế này luôn ở trạng thái opt-in trong dòng 1.x. Khi đó, biến này có giá trị mặc định là false và bạn phải đặt thành true. n8n 2.0 bật task runner theo mặc định. Vì vậy, trong dòng 2.x, guide này cố định biến đó là deprecated và tài liệu n8n cho biết bạn không còn cần đặt biến này nữa.

Khởi động lần đầu

docker compose up -d
docker compose ps
docker compose logs -f n8n

Lần boot đầu tiên thành công kết thúc bằng dòng Editor is now accessible via:, bên trên là dòng n8n ready on ..., port 5678. docker compose ps phải hiển thị cả hai container Up, trong đó postgres được đánh dấu (healthy). Nếu n8n rơi vào vòng lặp Restarting, hãy đọc log. Nguyên nhân gần như luôn là kết nối database hoặc quyền trên volume được trình bày bên dưới.

TLS với reverse proxy

Bản thân n8n sử dụng HTTP thường trên cổng 5678; một thành phần phía trước sẽ thực hiện TLS termination. Có 2 lựa chọn gọn.

Nếu bạn đã chạy nhiều container, hãy đặt n8n phía sau reverse proxy Traefik tự động cấp certificate TLS chỉ với một vài label. Traefik sẽ request và tự gia hạn certificate cho bạn.

Nếu đây là app duy nhất trên máy, một nginx virtual host với certificate Let's Encrypt sẽ đơn giản hơn. Dùng cấu hình Certbot và nginx TLS cho Ubuntu 24.04 để lấy certificate, sau đó dùng server block này:

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

    ssl_certificate     /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;

    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-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600;
        client_max_body_size 16m;
    }
}

Các header Upgrade và Connection "upgrade" là bắt buộc. n8n đẩy các cập nhật execution đang chạy đến editor qua WebSocket. Nếu thiếu 2 dòng này, trang đăng nhập sẽ tải xong rồi bị treo với banner mất kết nối. proxy_read_timeout 3600 ngăn các execution chạy lâu bị nginx ngắt sau 60 giây mặc định. Header X-Forwarded-Proto $scheme đi kèm với N8N_PROXY_HOPS=1: nó cho n8n biết request ban đầu sử dụng HTTPS dù proxy kết nối đến n8n qua HTTP thường. Nhờ đó, n8n không xác định kết nối là không an toàn rồi từ chối cookie của chính nó.

Workflow đầu tiên để chạy thực tế

Mở https://n8n.example.com/, tạo tài khoản owner (phần tiếp theo), rồi xây dựng workflow nhỏ nhất để xác nhận luồng hoạt động: nhận webhook, gọi HTTP và trả response.

  1. Thêm node Webhook. Đặt method là POST và path như hello. Node này hiển thị 2 URL: Test URL và Production URL. Đây là nguyên nhân của một nửa số báo cáo kiểu “webhook của tôi không hoạt động”. Test URL chỉ nhận 1 request và chỉ hoạt động khi bạn đã bấm Listen for test event; sau đó URL hết hiệu lực. Production URL được đăng ký khi bạn publish workflow và tiếp tục nhận request từ đó.
  2. Thêm node HTTP Request phía sau node này, trỏ đến bất kỳ public JSON API nào. Request GET đến https://api.github.com/zen trả về một chuỗi trên 1 dòng là đủ.
  3. Thêm node Respond to Webhook. Đặt tùy chọn Respond của node Webhook thành "Using Respond to Webhook node" để caller nhận lại output của HTTP node.
  4. Bấm Publish ở góc trên bên phải rồi gọi URL: curl -X POST https://n8n.example.com/webhook/hello. Bạn sẽ nhận lại dòng zen: POST đi vào, API được gọi và response được trả ra. Đây là cấu trúc của phần lớn automation thực tế. Các hướng dẫn cũ thường yêu cầu bạn bật toggle Active ở đây; n8n 2.0 đã thay toggle đó bằng Publish và Unpublish.

Phiên bản chạy theo lịch thay node Webhook bằng Schedule Trigger và gọi một model endpoint. Một model tự host bằng Ollama chạy trên cùng VPS là cách gọn để xây dựng công cụ tóm tắt chạy hằng đêm.

Quản lý người dùng, không phải basic auth

Các hướng dẫn n8n cũ yêu cầu bạn đặt N8N_BASIC_AUTH_ACTIVE=true. Những biến đó đã bị gỡ trong n8n 1.0 và hiện không còn tác dụng. Hiện nay, cơ chế xác thực là tài khoản owner: lần đầu bạn mở editor, n8n yêu cầu bạn tạo tài khoản owner bằng email và password, và bước chặn này là bắt buộc; không có chế độ anonymous. Hãy tạo tài khoản ngay sau lần boot đầu tiên, trước khi cung cấp URL cho bất kỳ ai: trong khoảng thời gian từ docker compose up đến lần gửi form đầu tiên, bất kỳ ai truy cập được instance cũng có thể chiếm quyền sở hữu. Thêm một lớp basic-auth ở reverse proxy là biện pháp khóa bổ sung hợp lý, nhưng đó là lớp thứ hai, không phải cơ chế xác thực chính. Tài khoản owner và mọi nội dung khác trong hướng dẫn này đều chạy được trên community edition miễn phí; nếu sau này bạn cần thêm user với role chi tiết hoặc SSO, hãy đọc những tính năng n8n nào cần license trả phí trước khi lập kế hoạch dựa trên các tính năng đó.

Sao lưu: ưu tiên key mã hóa, sau đó đến database

Có hai thứ cần sao lưu, và khả năng thay thế chúng không giống nhau.

N8N_ENCRYPTION_KEY. Mọi credential bạn lưu trong n8n, token API, mật khẩu database và secret OAuth đều được mã hóa khi lưu bằng key này. Workflow trong Postgres sẽ vô dụng nếu thiếu key: nếu restore database lên một máy mới bằng key khác, n8n không thể giải mã bất kỳ credential nào, đồng thời không có cách khôi phục hoặc reset. File .env chứa key này. Hãy copy file ra khỏi server ngay khi tạo, tốt nhất là lưu trong password manager. Đây mới là bản backup thực sự quan trọng.

Database Postgres, chứa workflow, lịch sử thực thi và chính các credential đã mã hóa:

docker compose exec -T postgres pg_dump -U n8n -d n8n \
  | gzip > n8n-db-$(date +%F).sql.gz

Hãy chạy lệnh này theo lịch và copy dump ra khỏi máy. Để restore trên VPS mới: khởi động stack một lần để tạo database, stop n8n, nạp lại dump bằng psql, đặt đúng N8N_ENCRYPTION_KEY vào .env, rồi start n8n. Key giống nhau cộng với dump sẽ tạo ra một instance hoạt động; key mới sẽ khiến các workflow không thể sử dụng bất kỳ credential nào.

Nâng cấp: cố định tag

File compose cố định n8nio/n8n:2.29.10 thay vì latest có chủ ý. n8n releases một bản minor mới trong hầu hết các tuần và đôi khi thay đổi database schema hoặc hành vi của node giữa các bản này. Vì vậy, latest có nghĩa là một lần pull không giám sát có thể cung cấp cho bạn một build thực hiện migration database ngay khi khởi động. Hãy cố định version, đọc ghi chú release trước khi tăng version. n8n nêu các breaking change tại đó. Sau đó nâng cấp có chủ đích:

docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > pre-upgrade.sql.gz
# edit the image tag in docker-compose.yml, then:
docker compose pull n8n
docker compose up -d n8n
docker compose logs -f n8n

Việc này đặc biệt quan trọng khi chuyển sang major version mới. Chẳng hạn, dòng 2.0 mặc định chuyển N8N_BLOCK_ENV_ACCESS_IN_NODE sang true. Vì vậy, mọi Code node đọc process.env sẽ âm thầm mất quyền truy cập cho đến khi bạn đặt lại thành false. Cùng release đó cũng bắt đầu thực thi quyền nghiêm ngặt trên settings file, bật task runner theo mặc định và thay toggle Activate workflow bằng Publish. Hãy đọc trang breaking change của 2.0 trước khi vượt qua ranh giới giữa các major version. n8n tự động chạy mọi database migration cần thiết khi khởi động. Đó chính là lý do pg_dump trước khi nâng cấp là bắt buộc. Vì credentials được mã hóa bằng một key trong .env còn dữ liệu nằm trong Postgres, các container có thể được xóa và tạo lại: bạn nâng cấp bằng cách thay thế chúng, rồi rollback bằng cách cố định tag trước đó và khôi phục dump.

Các lỗi thường gặp và chuỗi log tương ứng

The requested webhook "POST hello" is not registered. Nhận 404 khi gọi webhook của workflow chưa được publish, hoặc khi gọi test path lúc không có tiến trình nào đang listen. Test path (/webhook-test/...) chỉ trả lời khi bạn đã bấm "Listen for test event"; production path (/webhook/...) chỉ trả lời sau khi workflow được publish. Lỗi cùng nhóm This webhook is not registered for GET requests. Did you mean to make a POST request? có nghĩa là method không đúng: node yêu cầu POST nhưng bạn gửi GET.

URL webhook hiển thị :5678 hoặc localhost. Node hiển thị https://n8n.example.com:5678/webhook/... hoặc http://localhost:5678/.... WEBHOOK_URL chưa được set hoặc có giá trị sai, nên n8n dựng address từ N8N_HOST:N8N_PORT thay vì public base của bạn. Set WEBHOOK_URL=https://n8n.example.com/, tạo lại container bằng docker compose up -d, rồi port sẽ biến mất.

There was a problem loading init data trong browser. Editor đã load nhưng không thể truy cập backend API của chính nó. Khi chạy sau proxy, nguyên nhân gần như luôn là N8N_HOST hoặc WEBHOOK_URL sai, proxy thiếu các header WebSocket Upgrade, hoặc N8N_PROTOCOL không khớp với cách bạn kết nối. Xác nhận 4 biến public-facing và kiểm tra proxy forward Upgrade và Connection.

password authentication failed for user "n8n" trong log và container liên tục restart. Password mà n8n gửi không khớp với password database đã được khởi tạo. Điểm dễ nhầm là Postgres chỉ đọc POSTGRES_PASSWORD khi khởi tạo một data directory trống. Bạn start stack một lần, sau đó đổi POSTGRES_PASSWORD trong .env, nhưng volume postgres_data hiện tại vẫn chứa password cũ. Đặt lại password ban đầu. Nếu không cần giữ dữ liệu, hãy docker compose down và docker volume rm volume của Postgres, rồi khởi động lại từ đầu.

EACCES: permission denied, open '/home/node/.n8n/config' khi start. n8n chạy bằng user node (UID 1000) và không thể ghi vào config directory. Lỗi này thường xảy ra khi bạn bind-mount một folder trên host (./n8n_data:/home/node/.n8n) đang thuộc sở hữu của root. Dùng named volume đã nêu ở trên. Nếu bắt buộc dùng bind mount, hãy sudo chown -R 1000:1000 ./n8n_data trước.

Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. Từ dòng 2.x, n8n mặc định áp dụng 0600 cho settings file đó và tự sửa khi boot. Dòng log này có nghĩa là n8n đã sửa mode, thường sau khi bind mount hoặc sau khi restore sao chép file trở lại với permission quá rộng. Không cần làm gì thêm. Chỉ set N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false nếu filesystem của bạn thực sự không hỗ trợ permission.

Mismatching encryption keys, dòng đầy đủ cho biết encryption key trong settings file /home/node/.n8n/config không khớp với N8N_ENCRYPTION_KEY trong environment. Key trong environment khác với key mà n8n đã ghi vào data volume ở lần chạy trước. Nguyên nhân thường gặp nhất là n8n tự tạo key ngẫu nhiên ở lần boot trước khi biến này chưa được set, sau đó bạn set một key khác. Đặt lại key ban đầu trong .env. Chỉ khi bạn thực sự không có credential đã lưu nào cần giữ, hãy xóa file config bên trong volume n8n_data và để n8n tạo lại file, đồng thời chấp nhận rằng các credential hiện có sẽ không thể đọc được.

Thông báo khi login về secure cookie: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. Bạn đã set N8N_PROTOCOL=https nhưng truy cập n8n qua HTTP không mã hóa, thường do truy cập trực tiếp bằng IP và port thay vì qua HTTPS proxy. Hãy truy cập bằng https://n8n.example.com/. Chỉ set N8N_SECURE_COOKIE=false nếu bạn thực sự không thể dùng HTTPS, và tuyệt đối không làm vậy trên máy chủ public Internet.

Để đưa language model vào các workflow đó, xem cách xây dựng workflow AI với Claude và n8n.

FAQ

Tôi nên dùng SQLite hay Postgres cho n8n?

SQLite (mặc định) phù hợp để thử n8n và chạy một instance cá nhân, mỗi lần chỉ chạy một workflow. Hãy chuyển sang Postgres cho mọi hệ thống mà bạn phụ thuộc vào: single-writer lock của SQLite gây ra database is locked khi có concurrency, còn Postgres hỗ trợ backup sạch bằng pg_dump. Việc migrate sau này phải làm thủ công, nên nếu máy chủ quan trọng, hãy dùng Postgres ngay từ đầu.

Tại sao webhook của n8n không bao giờ chạy?

Gần như luôn là do WEBHOOK_URL. Khi biến này chưa được set hoặc có giá trị sai, n8n tạo các địa chỉ webhook từ N8N_HOST:N8N_PORT, thường chứa :5678 hoặc localhost. Các địa chỉ này trông có vẻ hợp lệ nhưng không thể truy cập từ Internet, nên request từ caller không bao giờ đến được. Hãy set WEBHOOK_URL=https://n8n.example.com/ và xác nhận node hiển thị URL không có port. Nguyên nhân thứ hai là bạn gọi production URL của một workflow chưa được publish, nên nhận The requested webhook ... is not registered.

Tôi phải backup những gì trong n8n?

Có 2 thứ. Thứ nhất là N8N_ENCRYPTION_KEY từ file .env, vì mọi credential được lưu đều được mã hóa bằng giá trị này. Nếu mất nó, các credential sẽ vĩnh viễn không thể giải mã. Hãy copy giá trị này ra khỏi server ngay trong ngày tạo. Thứ hai là pg_dump của database Postgres, bao gồm workflow, history và credential. Khi restore, bạn cần cả 2 thứ: đúng key và dump database.

Làm thế nào để đặt n8n phía sau HTTPS?

n8n cung cấp HTTP thuần trên port 5678; reverse proxy phía trước sẽ thực hiện TLS termination. Hãy bind n8n vào 127.0.0.1:5678 để chỉ proxy có thể truy cập, sau đó dùng Traefik với certificate tự động hoặc nginx với certificate từ Let's Encrypt. Set N8N_PROTOCOL=https và WEBHOOK_URL=https://your-host/, đồng thời bảo đảm proxy chuyển tiếp đúng các header Upgrade của WebSocket, nếu không editor sẽ bị treo.

Làm thế nào để upgrade n8n an toàn?

Hãy pin một image tag cụ thể thay vì latest, tạo pg_dump trước vì n8n tự động chạy migration khi khởi động, đọc release notes để kiểm tra breaking change, sau đó tăng tag và chạy docker compose pull n8n && docker compose up -d n8n. Container có thể bỏ đi, nên bạn có thể rollback bằng cách pin tag trước đó và restore dump trước khi upgrade.