n8n bản self-hosted cho phép trỏ AI Assistant sang một endpoint tương thích OpenAI bất kỳ. Tận dụng điều đó, bạn có thể đặt Claude Code phía sau một proxy nhỏ và dùng chính tài khoản Claude đã đăng nhập, thay vì nhúng API key Anthropic vào n8n.
Thank you for reading this post, don't forget to subscribe!Bài này ghi lại toàn bộ quá trình mình làm thật, gồm cả một lỗi ngốn mất nửa buổi mà nguyên nhân hoàn toàn không nằm ở AI.
Bạn cần chuẩn bị gì?
Cần hai máy Linux thấy nhau qua mạng nội bộ, hoặc một máy đóng cả hai vai. Mình chạy hai container LXC trong Proxmox, nhưng hai VPS hay hai máy ảo đều được.
Danh sách:
- Máy A: n8n bản self-hosted có tính năng AI Assistant, chạy bằng Docker
- Máy A: n8n Sandbox Service đang hoạt động — phần này bắt buộc, giải thích ở dưới
- Máy B: Ubuntu 22.04 hoặc 24.04, đã cài Claude Code và đăng nhập tài khoản Claude trả phí
- Máy B: Python 3 kèm
python3-venv - Biết dùng
systemdở mức cơ bản - Khoảng 45–60 phút
Kiểm tra Claude Code trước khi bắt đầu:
claude auth status
Kết quả phải có "loggedIn": true. Chưa có thì đăng nhập xong hãy làm tiếp, đừng vừa cài vừa sửa.
Chúng ta sẽ dựng cái gì?
Một gateway nhỏ nằm giữa n8n và Claude Code. Nhiệm vụ của nó là nhận request theo định dạng OpenAI, chuyển thành lệnh gọi Claude Code, rồi đóng gói câu trả lời lại đúng chuẩn OpenAI.
┌────────────────────────────┐ ┌────────────────────────────┐
│ MÁY A — n8n │ │ MÁY B — Claude │
│ │ HTTP │ │
│ n8n :5678 │───────▶│ Claude Proxy :8080 │
│ Sandbox API :8090 ★ │ │ │ │
│ SearXNG :8888 │ │ ▼ │
│ pgvector :5432 │ │ Claude Code (đã login) │
└────────────────────────────┘ └────────────────────────────┘
★ Sandbox API không phải thành phần tùy chọn. Nhớ dấu sao này, phần cuối bài sẽ quay lại.
Với n8n, proxy trông y hệt một endpoint OpenAI. Với Claude, đây chỉ là các lệnh claude -p bình thường. Không có API key Anthropic nào xuất hiện trong n8n.
Cài đặt proxy trên máy B
Proxy là một ứng dụng FastAPI phơi ra ba endpoint: GET /, GET /v1/models và POST /v1/chat/completions. Bên trong, mỗi request sinh ra một tiến trình claude chạy bất đồng bộ.
Bước 1: Tạo môi trường
mkdir -p /opt/claude-proxy/workspace && cd /opt/claude-proxy
apt update && apt install -y python3-venv python3-pip
python3 -m venv venv && ./venv/bin/pip install --upgrade pip fastapi uvicorn
Bước 2: Cấp token dài hạn cho Claude Code
claude setup-token
Token có hạn một năm. Hai điều cần nhớ ngay lúc này.
Thứ nhất, terminal in token ngắt thành hai dòng nhưng nó là một chuỗi liền — dán vào file cấu hình phải nối lại. Thứ hai, đừng bao giờ gõ token vào dòng lệnh, vì mọi lệnh đều lưu vào ~/.bash_history.
Bước 3: Tách bạch hai loại secret
Đây là chỗ mình từng nhầm và trả giá bằng một loạt lỗi 401.
| Secret | Dạng giá trị | Ai dùng |
|---|---|---|
PROXY_API_KEY |
chuỗi hex 64 ký tự | n8n dùng để gọi vào proxy |
CLAUDE_CODE_OAUTH_TOKEN |
bắt đầu bằng sk-ant-oat01- |
proxy dùng để gọi Claude |
Sinh khoá proxy ghi thẳng vào file, không in ra màn hình:
sed -i "s|^PROXY_API_KEY=.*|PROXY_API_KEY=$(openssl rand -hex 32)|" /etc/claude-proxy.env
Rồi mở file dán token Claude vào:
nano /etc/claude-proxy.env
Đặt quyền chặt cho file này, vì nó giữ cả hai secret:
chmod 600 /etc/claude-proxy.env
Bước 4: Chạy dưới systemd, đừng dùng nohup
Lúc thử nghiệm mình chạy nohup uvicorn ... & cho nhanh. Sai lầm. Reboot một cái là proxy biến mất, mà token export bằng tay cũng bay theo shell.
Bản chính thức cần một unit systemd với ba thứ:
Environment=HOME=/root
EnvironmentFile=/etc/claude-proxy.env
Restart=always
Dòng HOME=/root dễ bị bỏ sót nhất. Thiếu nó, claude không tìm thấy credential và mọi request đều lỗi, dù bạn đã đăng nhập thành công ở terminal.
systemctl enable --now claude-proxy
Kết quả mong đợi:
Active: active (running)
LISTEN 0 2048 0.0.0.0:8080
Chú ý: nếu trước đó bạn từng chạy bản
nohup, tiến trình cũ vẫn đang giữ port 8080. Phảipkill -f 'uvicorn main:app'trước khi bật service, không thì systemd khởi động thất bại mà thông báo lỗi khá mơ hồ.
Vì sao proxy bắt buộc phải hỗ trợ streaming?
AI Assistant của n8n gọi endpoint theo kiểu Server-Sent Events. Proxy chỉ trả JSON một cục thì khung chat hiện ra rồi đứng im, không báo lỗi gì cả — kiểu hỏng khó chịu nhất vì chẳng có manh mối.
Proxy của mình xử lý bằng cách chờ Claude trả lời xong rồi mới cắt nhỏ phát ra dạng SSE. Không phải streaming token-by-token thật, nhưng đúng chuẩn với phía client.
Có một chi tiết dễ bỏ qua: claude -p mất 30 đến 120 giây tùy câu hỏi. Nếu proxy im lặng suốt thời gian đó, client sẽ ngắt kết nối trước khi nhận được byte đầu tiên. Cách xử lý là mở stream ngay lập tức bằng một chunk rỗng, rồi cứ 10 giây gửi một dòng : keepalive để giữ kết nối.
Kiểm tra bằng lệnh này, thay <IP_CLAUDE> bằng IP nội bộ máy B:
curl -N http://<IP_CLAUDE>:8080/v1/chat/completions -H "Authorization: Bearer <PROXY_KEY>" -H "Content-Type: application/json" -d '{"model":"claude","stream":true,"messages":[{"role":"user","content":"hi"}]}'
Phải thấy các dòng data: {...} rồi kết thúc bằng data: [DONE]. Thiếu [DONE] là client sẽ treo.
Cấu hình phía n8n
Vào Settings, phần AI Assistant:
| Trường | Giá trị |
|---|---|
| Provider | Self-hosted or OpenAI-compatible endpoint |
| Base URL | http://<IP_CLAUDE>:8080/v1 |
| API Key | giá trị PROXY_API_KEY |
| Model | claude |
Nếu ô Base URL của n8n tự thêm /chat/completions thì đừng gõ thêm đuôi đó nữa. Lỗi nhầm port cũng hay gặp: 8080 là proxy, 8090 là Sandbox, 8888 là SearXNG.
Nghiệm thu theo tầng thay vì thử ngẫu nhiên
Hệ thống này có năm tầng. Test lung tung sẽ mất thời gian gấp đôi. Chạy tuần tự, hỏng ở đâu sửa ở đó:
- Claude CLI —
claude -p "Chỉ trả lời OK"trên máy B - Proxy tại chỗ — gọi
127.0.0.1:8080ngay trên máy B - Từ máy A sang máy B — gọi qua IP nội bộ, để loại trừ firewall
- Từ trong container Docker của n8n — network khác host, phải thử riêng
- Sandbox Service — tạo thử một sandbox, phải trả
HTTP 201
Tầng 4 hay bị bỏ sót. n8n chạy trong Docker nên việc host gọi được không đồng nghĩa container gọi được:
docker exec n8n sh -c 'wget -qO- http://<IP_CLAUDE>:8080/health'
Bài học đắt nhất: log trống nghĩa là gì?
Đến đây hệ thống chạy ngon. Mình hỏi vài câu trong AI Assistant, Claude trả lời mượt. Xong việc, đi ăn cơm.
Quay lại mở một cuộc trò chuyện mới thì n8n báo:
Something went wrong before I could finish that response. Please try again.
Phản xạ đầu tiên của mình là nghi proxy. Mình thêm logging chi tiết, ghi lại nguyên văn mọi request, restart service, thử lại. Và phát hiện một điều lạ: log của proxy trống trơn. Không một dòng nào. n8n báo lỗi nhưng chưa từng gửi request nào sang.
Đó chính là manh mối. Log trống nghĩa là lỗi nằm ở tầng trên, không phải tầng dưới. Mình chuyển sang đọc log của n8n:
docker logs --tail=150 n8n 2>&1 | tail -80
Và nó hiện ra:
Instance AI run error: no sandbox runners are registered or available
SandboxServiceError: no sandbox runners are registered or available
at Workspace.init (...)
Hoá ra AI Assistant của n8n khởi tạo một sandbox workspace trước khi gọi LLM. Sandbox runner đã chết âm thầm sau lần host khởi động lại. Nó chết ở bước Workspace.init, tức là chưa bao giờ đi tới đoạn gọi Claude.
Cả buổi mổ xẻ proxy, trong khi proxy hoàn toàn vô tội.
Vì sao cuộc trò chuyện đầu tiên vẫn chạy được? Vì chat hỏi đáp đơn giản chưa cần workspace. Phiên có panel Artifacts và Connections mới cần, và đó là lúc nó gãy.
Cách sửa nằm ở chỗ hai khoá trong file .env của Sandbox phải khớp nhau — SANDBOX_API_RUNNER_API_KEY phải bằng SANDBOX_RUNNER_API_KEYS. Lệch là runner không đăng ký được:
docker compose up -d --force-recreate api runner
Đợi khoảng 30 giây rồi tạo thử một sandbox, phải ra HTTP/1.1 201 Created kèm "status":"running".
Rút ra: trong hệ thống nhiều tầng, thứ không xuất hiện trong log có giá trị chẩn đoán ngang với thứ xuất hiện. Trước khi sửa một thành phần, hãy xác nhận request có thực sự chạm tới nó chưa.
Bảng lỗi thường gặp
| Hiện tượng | Nguyên nhân | Cách sửa |
|---|---|---|
| Log proxy trống khi n8n báo lỗi | Sandbox runner chết | Dựng lại api và runner, đồng bộ hai khoá |
Trả 401 Invalid API key |
Nhầm PROXY_API_KEY với OAuth token |
Khoá proxy là chuỗi hex 64 ký tự |
Trả 502 claude_error |
Token hết hạn hoặc hết quota | Cấp token mới, xem dòng claude stderr trong log |
| Khung chat hiện nhưng không ra chữ | Proxy không phát SSE | Kiểm tra response có kết thúc bằng data: [DONE] |
| Service chết sau reboot | Còn chạy bằng nohup |
Chuyển sang systemd, đặt Restart=always |
| Proxy chạy nhưng mọi request lỗi auth | Thiếu HOME=/root trong unit |
Thêm dòng Environment=HOME=/root |
| Console đăng nhập được, SSH thì không | Đang thử mật khẩu của máy khác | Đối chiếu hostname với IP trước khi nghi cấu hình SSH |
Giới hạn cần biết trước khi làm
Không phải giải pháp nào cũng hợp mọi tình huống. Ba điểm mình muốn nói thẳng.
Không hỗ trợ function calling. Proxy chỉ trả text thuần. Đủ dùng cho AI Assistant hỏi đáp, nhưng node AI Agent trong workflow sẽ lỗi nếu có gắn tool. Muốn dùng phải viết thêm phần dịch tools sang định dạng của Claude Code và trả về tool_calls.
Vẫn chịu rate limit của gói thuê bao. Backend là tài khoản Claude trả phí thông qua Claude Code, nên chạy tự động khối lượng lớn sẽ chạm trần. Đến ngưỡng đó thì chuyển sang API key Anthropic là hợp lý hơn.
Chỉ nên chạy trong LAN. Các port 8080, 8090, 8888, 5432 đều không nên phơi ra Internet. Nếu máy có IP public, đặt firewall chỉ cho IP của máy n8n gọi vào.
Câu hỏi thường gặp
Có bắt buộc phải dùng hai máy không?
Không. Gộp cả hai vai trò vào một máy vẫn chạy, chỉ cần đổi Base URL trong n8n thành http://127.0.0.1:8080/v1. Tách hai máy giúp Claude Code không giành tài nguyên với n8n khi workflow chạy nặng.
Vì sao không dùng thẳng API key Anthropic cho nhanh?
Vì cách này tận dụng tài khoản thuê bao đã có sẵn, không phát sinh chi phí theo token. Đổi lại là chịu rate limit của gói và không có function calling. Nếu bạn cần chạy sản xuất khối lượng lớn thì API key vẫn là lựa chọn đúng.
Streaming giả lập có ảnh hưởng trải nghiệm không?
Có một chút. Bạn chờ Claude trả lời xong mới thấy chữ bắt đầu chảy ra, thay vì hiện dần từng token. Với AI Assistant trong UI thì khác biệt không đáng kể, vì phần lớn câu trả lời hoàn tất trong vài giây.
Token Claude Code sống được bao lâu?
Token do claude setup-token cấp có hạn một năm. Nên đặt nhắc lịch gia hạn, vì lúc hết hạn triệu chứng sẽ là lỗi 502 khá khó hiểu nếu bạn không nhớ mốc thời gian.
Nếu lỡ để lộ token thì làm gì?
Cấp token mới ngay bằng claude setup-token, cập nhật vào file cấu hình, restart service, rồi xoá vết trong ~/.bash_history. Token cũ có thể vẫn còn hiệu lực nên đừng coi việc cấp lại là đủ, hãy kiểm tra phần cài đặt tài khoản.
Việc nối Claude vào n8n self-hosted không khó về mặt kỹ thuật. Phần khó nằm ở ba chỗ ít ai nói tới: streaming SSE, chạy service đúng cách, và hiểu rằng AI Assistant cần Sandbox sống mới hoạt động.
Nếu bạn định làm theo, mình khuyên hai điều. Một là nghiệm thu theo tầng ngay từ đầu, đừng đợi hỏng mới lần ngược. Hai là bật logging chi tiết cho proxy trước khi cắm vào n8n — lúc có sự cố, biết được request có tới hay không thường quan trọng hơn biết nội dung request là gì.
Bạn đang tự host n8n và muốn thử cách này? Để lại bình luận, mình gửi bộ file triển khai kèm script kiểm tra tự động.
Download file md gửi cho AI để hướng dẫn cài đặt : https://drive.google.com/file/d/1XKxYzJIA87S6iBdL4daFle0PCJjbY4R-/view

