3A.COM.VN

Agent & Tự Động Hóa

n8n queue mode: 6 container, pool Postgres chỉ 2 kết nối

Self host n8n queue mode theo file mẫu chính hãng cần 6 container. Mỗi worker mặc định chạy 10 job song song nhưng pool Postgres mặc định chỉ 2 kết nối. Bài không có số đo thông lượng hay p50/p90/p99, chỉ dẫn lệnh để bạn tự đo.

Sản phẩm trong bài:
n8nPostgreSQL
Quang Minh· Trưởng ban Nghiên cứu & Đo kiểm AI12 phút đọc23/10/20260 lượt xem
n8n queue mode: 6 container, pool Postgres chỉ 2 kết nối
n8nPostgreSQL

Câu hỏi thực nghiệm của bài: khi self host n8n queue mode trên một VPS, cần dựng đúng những gì, và cấu hình mặc định có mâu thuẫn nào khiến worker chậm trước cả khi tải tăng?

Kết luận từ tài liệu chính hãng, chưa phải từ phép đo: file Docker Compose mẫu của n8n gồm 6 service (Postgres, Redis, main, worker và 2 task runner). Mỗi worker mặc định nhận 10 job song song, trong khi pool kết nối Postgres mặc định chỉ là 2. Một response lớn đang truyền qua Redis có thể chiếm tới 96 MiB RAM (64 MiB x 1,5). Bài không đưa số thông lượng hay độ trễ hàng đợi p50/p90/p99. Tôi chưa tìm được số đo công khai nào có đủ 2 nguồn và ghi rõ cấu hình máy, nên mục cuối hướng dẫn bạn tự đo trên máy của mình.

Bài chỉ bàn phần cài đặt và vận hành worker. Kiến trúc agent chăm sóc khách hàng chạy trên n8n đã có ở bài Xây agent chăm sóc khách hàng với n8n, không lặp lại ở đây.

Dự án nội bộ · 3A AI Stack Advisor

Đừng trả tiền thừa cho SaaS AI — Nhận lộ trình công cụ & chi phí tối ưu trong 10 giây

Mô tả bài toán của doanh nghiệp bạn (CSKH Zalo OA, Coding, RAG tài liệu nội bộ, Báo cáo P&L). Hệ thống cố vấn của 3A sẽ đề xuất bộ công cụ đã qua kiểm thử thực chiến.

Mọi dữ kiện trong bài lấy từ tài liệu chính hãng của n8n. Danh sách link kèm ngày đọc nằm ở mục Nguồn tham khảo cuối bài.

Self host n8n queue mode chạy những tiến trình nào?

Ở chế độ mặc định, một tiến trình n8n vừa nhận trigger vừa chạy workflow. Bật EXECUTIONS_MODE=queue thì việc tách ra: main nhận trigger và webhook rồi đẩy ID execution vào Redis, worker lấy job từ Redis, đọc dữ liệu workflow từ Postgres, chạy xong thì ghi kết quả lại Postgres.

Thành phầnVai tròBắt buộc?Nguồn
PostgreSQL
Lưu workflow, credential, lịch sử executionCó. Docs ghi queue mode với SQLite "không được khuyến nghị", hệ phân tán trên SQLite "không được hỗ trợ"Docs queue mode
RedisHàng đợi job (Bull)CóDocs queue mode
Main (n8n)
Giao diện, trigger, đưa job vào hàng đợiCóDocs queue mode
Worker (n8n worker)
Chạy executionCó, từ 1 trở lênDocs queue mode
Webhook processor (n8n webhook)
Nhận /webhook/* sau load balancerKhông, dùng khi webhook nhiềuDocs queue mode
Task runner (n8nio/runners)
Chạy node Code ở tiến trình riêngCó trong file mẫu chính hãngCompose n8n-hosting

n8n postgres docker compose: file mẫu chính hãng có gì?

Khối dưới đây chép nguyên văn file docker-compose.yml trong repo n8n-io/n8n-hosting, thư mục withPostgresAndWorker, tải ngày 19/10/2026 lúc 11h10 GMT+7. Tôi không sửa dòng nào. Giá trị thật để trong file .env cùng thư mục, gồm N8N_VERSION, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_NON_ROOT_USER, POSTGRES_NON_ROOT_PASSWORD, ENCRYPTION_KEY, RUNNERS_AUTH_TOKEN. 3A chưa chạy file này trên VPS.

yaml
volumes:
  db_storage:
  n8n_storage:
  redis_storage:

x-shared: &shared
  restart: always
  image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
  environment:
    - DB_TYPE=postgresdb
    - DB_POSTGRESDB_HOST=postgres
    - DB_POSTGRESDB_PORT=5432
    - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
    - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
    - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
    - EXECUTIONS_MODE=queue
    - QUEUE_BULL_REDIS_HOST=redis
    - QUEUE_HEALTH_CHECK_ACTIVE=true
    - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
    - N8N_RUNNERS_MODE=external
    - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
    - N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
    - OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true
  volumes:
    - n8n_storage:/home/node/.n8n
  depends_on:
    redis:
      condition: service_healthy
    postgres:
      condition: service_healthy

x-runner: &runner
  restart: always
  image: n8nio/runners:${N8N_VERSION}
  environment:
    - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}

services:
  postgres:
    image: postgres:18
    restart: always
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
      - POSTGRES_DB
      - POSTGRES_NON_ROOT_USER
      - POSTGRES_NON_ROOT_PASSWORD
      # postgres:18 moved its default data dir. Pinning it keeps the volume
      # mount stable across major versions.
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - db_storage:/var/lib/postgresql/data
      - ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - redis_storage:/data
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    <<: *shared
    ports:
      - 5678:5678

  n8n-runner:
    <<: *runner
    environment:
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
      - N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
    depends_on:
      - n8n

  n8n-worker:
    <<: *shared
    command: worker
    depends_on:
      - n8n

  n8n-worker-runner:
    <<: *runner
    environment:
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
      - N8N_RUNNERS_TASK_BROKER_URI=http://n8n-worker:5679
    depends_on:
      - n8n-worker

Ba điểm trong file mẫu dễ bị bỏ qua khi tự viết compose:

1. Main và worker dùng chung một anchor &shared. Nhờ vậy N8N_ENCRYPTION_KEY luôn giống nhau. Docs ghi rõ khoá mã hoá của main phải được chia sẻ cho mọi worker và webhook processor; lệch khoá thì worker không giải mã được credential. 2. OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true đẩy cả lần bấm chạy thử trong editor sang worker. Giá trị mặc định của biến này là false. 3. Postgres 18 đổi thư mục dữ liệu mặc định, file mẫu ghim PGDATA=/var/lib/postgresql/data để volume không lệch khi nâng bản lớn.

n8n queue mode redis: biến môi trường nào cần đặt?

Bảng dưới lấy từ trang biến môi trường queue mode và database của n8n. Cột "Mặc định" là giá trị docs công bố, không phải giá trị tôi khuyến nghị.

BiếnMặc địnhGhi chú vận hành
EXECUTIONS_MODEkhông đặt (chế độ thường)Đặt queue trên main và mọi worker
QUEUE_BULL_REDIS_HOST / _PORTlocalhost / 6379Trong compose là tên service redis
QUEUE_BULL_REDIS_PASSWORDtrốngNên đặt nếu Redis không nằm trong mạng nội bộ của Docker
QUEUE_BULL_REDIS_TIMEOUT_THRESHOLD10000 msHết ngưỡng này mà Redis vẫn không phản hồi thì n8n thoát
QUEUE_BULL_REDIS_CLUSTER_NODEStrốngĐặt biến này thì n8n bỏ qua HOST và PORT
QUEUE_HEALTH_CHECK_ACTIVEfalseBật để có /healthz và /healthz/readiness
QUEUE_WORKER_LOCK_DURATION60000 msThời gian worker giữ lease một job
QUEUE_WORKER_STALLED_INTERVAL30000 msChu kỳ quét job kẹt, đặt 0 là tắt
N8N_GRACEFUL_SHUTDOWN_TIMEOUT
30 giâyThay cho QUEUE_WORKER_TIMEOUT đã bỏ từ bản 1.22.0
N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX
64 MiBRedis cần khoảng 1,5 lần giá trị này cho mỗi response đang truyền
DB_POSTGRESDB_POOL_SIZE
2Số kết nối Postgres song song mỗi tiến trình
DB_POSTGRESDB_CONNECTION_TIMEOUT
20000 msHết hạn khi chờ kết nối

Giới hạn N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX cũng áp cho kết quả tool của node MCP Trigger. Nếu bạn dùng n8n làm MCP server (xem MCP là gì), tool trả về file lớn sẽ chạm trần này trước.

n8n worker scaling: concurrency bao nhiêu là vừa?

Docs công bố ba dữ kiện về concurrency:

  • Mặc định mỗi worker chạy 10 job song song.
  • n8n khuyến nghị đặt concurrency từ 5 trở lên.
  • Đặt concurrency thấp nhưng chạy nhiều worker có thể làm cạn pool kết nối database, gây chậm và lỗi.

Đổi concurrency bằng tham số của lệnh worker. Cú pháp lấy từ docs queue mode, 3A chưa chạy thử:

bash
# Chưa chạy thử. Ghi đè command của service n8n-worker
n8n worker --concurrency=5

Chỗ tôi muốn đo nhất là khoảng cách giữa 10 job mỗi worker và pool Postgres mặc định 2 kết nối. Docs không nêu 10 job tranh nhau 2 kết nối thì độ trễ ghi execution tăng bao nhiêu, nên bài không suy ra con số nào. Cách tự kiểm số kết nối đang dùng nằm ở mục cuối.

Thêm worker thì phải thêm cả runner. Trang task runner của n8n ghi: "When using Queue mode, each worker needs to have its own sidecar container for task runners." File mẫu chỉ có 1 cặp n8n-worker và n8n-worker-runner, runner trỏ cố định tới http://n8n-worker:5679. Vì vậy docker compose up -d --scale n8n-worker=3 sẽ tạo 3 worker nhưng vẫn chỉ có 1 runner, trái với yêu cầu trên. Docs không nêu worker thiếu runner sẽ báo lỗi gì, bài không suy đoán. Cách đúng theo docs là khai báo từng cặp worker và runner thành service riêng. Khối dưới do tôi viết theo mẫu của file gốc, chưa chạy thử:

yaml
# Chưa chạy thử
  n8n-worker-2:
    <<: *shared
    command: worker
    depends_on:
      - n8n

  n8n-worker-runner-2:
    <<: *runner
    environment:
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
      - N8N_RUNNERS_TASK_BROKER_URI=http://n8n-worker-2:5679
    depends_on:
      - n8n-worker-2

Cũng theo trang đó, nếu đặt OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=false thì main tự chạy lần chạy thử và cần runner riêng. Docs không khuyến nghị tắt offload khi chạy production.

Tự đo self host n8n queue mode trên máy của bạn như thế nào?

Bài không có bảng thông lượng. Trang Measure performance của n8n có 2 benchmark mẫu chạy trên máy AWS c5a và MySQL. Cấu hình này khác file compose mẫu (Postgres, 1 worker), và ngoài n8n chưa có nguồn thứ hai đo lại, nên bài không chép số. Dưới đây là 4 lệnh để bạn tự xem cụm của mình, kèm ngưỡng đối chiếu lấy từ docs. Các lệnh này chưa chạy trên cụm thật của 3A.

bash
# Chưa chạy thử trên cụm của 3A
# 1. RAM và CPU từng container, chạy trong lúc có tải
docker stats --no-stream

# 2. Số kết nối Postgres đang mở
docker compose exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" \n  -c "SELECT count(*) FROM pg_stat_activity WHERE datname = current_database();"

# 3. RAM Redis đang dùng
docker compose exec redis redis-cli INFO memory | grep used_memory_human

# 4. Worker đã nối được Postgres và Redis chưa (cần QUEUE_HEALTH_CHECK_ACTIVE=true)
docker compose exec n8n-worker node -e \n  "fetch('http://localhost:5678/healthz/readiness').then(r => console.log(r.status))"
LệnhChỉ số cần xemNgưỡng đối chiếu theo docs
docker statsCột MEM USAGE của 6 containerDocs không công bố mức RAM cho cụm này. Ghi lại để so giữa các lần đổi cấu hình
pg_stat_activitySố kết nối đang mởDB_POSTGRESDB_POOL_SIZE (mặc định 2) nhân số tiến trình n8n. Docs cảnh báo nhiều worker concurrency thấp có thể làm cạn pool
redis-cli INFO memoryused_memory_humanMỗi response đang truyền cần khoảng 1,5 lần N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX, mặc định 64 MiB nên tới 96 MiB
/healthz/readiness trên workerMã HTTPDocs: endpoint của worker server, báo kết nối DB và Redis của worker đã sẵn sàng. Cổng mặc định QUEUE_HEALTH_CHECK_PORT là 5678

Nếu bạn muốn tự đo thông lượng và p50/p90/p99 của độ trễ hàng đợi, có thể dùng khung dưới đây. Đây là cấu hình gợi ý, 3A chưa chạy khung này nên bài không có kết quả nào đi kèm:

text
# Chưa chạy thử
Region        : AWS ap-southeast-1 (Singapore)
Phiên bản     : n8n 2.43.7, postgres:18, redis:7-alpine (theo compose mẫu)
Máy bắn tải   : 1 VM riêng cùng region, k6 gọi webhook
Workflow mẫu  : Webhook -> Code (JSON 2 KB) -> Postgres INSERT -> Respond
Cỡ mẫu        : N = 30 lượt cho mỗi cấu hình, mỗi lượt 1.000 request
Biến thay đổi : số worker {1, 2, 4} x concurrency {5, 10} x POOL_SIZE {2, 10}
Chỉ số        : executions/phút; độ trễ hàng đợi p50 / p90 / p99; tỉ lệ lỗi

Giới hạn của phép đo và rủi ro vận hành

Cảnh báo kỹ thuật

Giới hạn áp dụng. Mọi số trong bài này là giá trị mặc định do n8n công bố, không phải kết quả đo. Bài không có số thông lượng hay độ trễ. Đừng dùng bài này để ước tính số VPS cần mua.

Giới hạn của phép đo khi bạn tự đo theo khung trên:

  • Workflow mẫu chỉ có 4 node. Workflow gọi API ngoài (OpenAI, Zalo, Google Sheets) bị chặn bởi độ trễ mạng của bên thứ ba, nên kết quả sẽ khác.
  • Đo trong cùng region ap-southeast-1. Người dùng gọi webhook từ Việt Nam sẽ cộng thêm độ trễ mạng từ Việt Nam sang Singapore, chưa đo.
  • N = 30 lượt đủ để ước lượng p50 và p90. Với p99, mỗi lượt cần ít nhất vài trăm mẫu thì đuôi phân phối mới ổn định, nên mỗi lượt bắn 1.000 request.

Rủi ro và chi phí ẩn khi chạy production:

Rủi roNguồnHậu quảCách giảm
Binary data ở chế độ filesystemDocs: n8n không hỗ trợ queue mode với binary data lưu filesystemWorker không đọc được file do main ghiDùng S3 (docs khuyến nghị) hoặc database
Response lớn đi qua RedisDocs: cần khoảng 1,5 lần RELAY_SIZE_MAX mỗi responseMặc định 64 MiB thì một response tốn tới 96 MiB RAM RedisGiảm N8N_WEBHOOK_RESPONSE_RELAY_SIZE_MAX hoặc bật offload (từ 2.34.0)
Redis mất kết nốiDocs: quá QUEUE_BULL_REDIS_TIMEOUT_THRESHOLD thì n8n thoátMain và worker cùng dừngrestart: always, healthcheck, Redis có persistence
Lệch phiên bản giữa main và workerDocs multi-main yêu cầu cùng phiên bảnLỗi khó đoán sau khi nâng cấp từng phầnGhim N8N_VERSION trong .env, nâng cấp cả cụm
Multi-mainDocs: chỉ có ở bản Enterprise tự hostBản community chỉ có 1 main, main chết thì UI và trigger dừngChấp nhận 1 main, giám sát /healthz
Màn hình Settings > WorkersDocs: Enterprise mới xem được trên bản tự hostBản community không thấy worker nào đang sốngDùng /healthz và /metrics của worker

Chi phí ẩn dễ quên nhất là RAM. So với chạy một tiến trình, file mẫu queue mode thêm 1 Postgres, 1 Redis, 1 worker và 2 runner, tức 5 container. Docs không công bố mức RAM cho cụm 6 container này. Cách tự đo: chạy docker stats --no-stream mỗi 10 giây trong lúc có tải, rồi lấy p90 của cột MEM USAGE.

Khi nào chưa nên bật n8n queue mode?

Queue mode giải quyết một việc: tách việc nhận trigger khỏi việc chạy workflow để thêm worker khi tải tăng. Nếu một tiến trình n8n đang chạy ổn và bạn chưa thấy execution xếp hàng, chi phí vận hành thêm Redis, Postgres và các cặp worker-runner lớn hơn lợi ích.

Ba dấu hiệu nên chuyển sang queue mode, đều kiểm được trên máy đang chạy:

1. Webhook trả về chậm khi có workflow nặng chạy cùng lúc. 2. Editor chậm hoặc treo trong giờ cao điểm. 3. Bạn cần khởi động lại tiến trình chạy workflow mà không làm gián đoạn trigger.

Nếu chưa có cả ba dấu hiệu, hãy chuyển từ SQLite sang Postgres trước. Bước này cũng là điều kiện bắt buộc của queue mode, làm trước thì lúc bật queue mode chỉ còn thêm Redis và worker.

Nguồn tham khảo

Nguồn tham khảo

Ghi chú

Cả 7 liên kết chính thức đều được kiểm tra phản hồi HTTP 200 lúc 11h10 ngày 19/10/2026.

Tài liệu / NguồnNội dung đối chiếuLiên kết chính hãng
n8n Docs
Enable queue modescaling/enable-queue-mode
n8n Docs
Queue mode environment variablesbasic-config/queue-mode
n8n Docs
Database environment variablesbasic-config/database
n8n Docs
Set up task runnersset-up-task-runners
n8n Docs
Measure performancescaling/measure-performance
GitHub n8n-hosting
File Compose mẫu withPostgresAndWorkerdocker-compose.yml
GitHub n8n-io/n8n
Bản phát hành mới nhất n8n@2.43.7n8n@2.43.7
  • Quang Minh, từ phòng đo kiểm 3A.
— Quang Minh, từ phòng đo kiểm 3A.

Bài liên quan