SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-13

Self-host open-kritt trên VPS bằng Docker Compose

Hướng dẫn chạy open-kritt trên VPS: pin release, dựng bằng Docker Compose, mở UI qua SSH tunnel tới port 5173 và đặt budget provider trước lần scan đầu.

Vì sao nên self-host open-kritt trên VPS thay vì laptop

Hãy self-host open-kritt trên một server mà bạn có thể xóa và dựng lại. Công cụ này chạy các agent phân tích dưới quyền root bên trong các job container dùng một lần, cấp cho mỗi agent một bản sao code có quyền ghi và quyền truy cập Internet trực tiếp, đồng thời mount Docker socket của host vào engine service. Đây là một đánh đổi hợp lý trên một máy chỉ dành cho công việc này. Nhưng trên máy chứa SSH keys của bạn thì đó là một lựa chọn tệ.

Bốn đặc điểm của thiết lập mặc định dẫn đến khuyến nghị này. Cả bốn đều xuất phát từ README và compose file của dự án.

Các agent được thiết kế để có quyền năng lớn. README cho biết các agent có tool chạy dưới quyền root bên trong các job container dùng một lần, với bản sao repository có quyền ghi và quyền truy cập Internet trực tiếp. Nhờ đó, chúng có thể cài tool, compile target, chạy test và tạo proof of concept. Một lần scan không chỉ là linter đọc file. Đó là việc thực thi code tùy ý mà bạn đã yêu cầu. Quyền truy cập Internet này có rủi ro theo cả hai chiều: mọi nội dung agent tải xuống trong quá trình nghiên cứu một target đều là text không đáng tin cậy đi vào prompt của agent, giống với rủi ro khi bạn cho agent tự thực hiện tìm kiếm trên web.

Engine giữ Docker socket. docker-compose.yml mount Docker socket của host vào engine service, vì engine build và khởi chạy một scan container cho mỗi job. Bất kỳ process nào truy cập được socket đó đều có thể start một container mount filesystem của host. Vì vậy, engine về thực chất có quyền root trên host chạy nó.

Không có màn hình đăng nhập. Backend được phát hành mà không có application authentication. Truy cập được vào port đồng nghĩa với truy cập được vào các phát hiện của bạn và khoản credit của provider.

Code bạn scan thường không thuộc về bạn. Trỏ agent vào một repository của bên thứ ba có nghĩa là chạy build của repository đó trên máy của bạn, dưới quyền root và có quyền truy cập mạng.

Nếu bạn đã đọc vì sao coding agent nên chạy trong VM dùng một lần, thì đây là cùng một threat model, chỉ mạnh hơn. Hãy cấp cho open-kritt một VPS không chạy dịch vụ nào khác, rồi điều khiển VPS đó từ một user account có quyền tối thiểu riêng biệt thay vì root.

open-kritt thực sự làm gì

open-kritt (repository là Kritt-ai/open-kritt, được cấp phép theo AGPL-3.0) chia quá trình nghiên cứu lỗ hổng thành các tác vụ nhỏ, chạy các tác vụ đó song song trên nhiều AI agent, sau đó loại bỏ kết quả trùng lặp và xếp hạng các kết quả còn lại. Bạn định nghĩa workflow dưới dạng chuỗi prompt tập trung. Mỗi bước nhận context có cấu trúc từ các bước trước đó. Đối tượng scan là một git repository từ xa hoặc cục bộ. Engine phân tích là Codex hoặc Claude Code. Sau khi xuất hiện một candidate, các post-script tùy chọn có thể thử xác thực candidate đó hoặc tạo proof of concept.

Kết quả cuối cùng là một danh sách candidate đã được xếp hạng. Hãy xem danh sách này là hàng đợi triage, không phải report.

Những thứ cần chuẩn bị trước khi bắt đầu

  • Một VPS chạy Ubuntu 24.04, Debian 12 hoặc Rocky Linux 9. Tài liệu cài đặt liệt kê đây là các bản phân phối đã được kiểm thử trên x86_64 và ARM64.
  • Docker Engine kèm Compose plugin.
  • Node.js 20 trở lên trên host, vì CLI ./kritt chạy trên host thay vì bên trong container.
  • Một nhà cung cấp model: tài khoản Codex hoặc OPENAI_API_KEY, CODEX_API_KEY, ANTHROPIC_API_KEY hay OPENROUTER_API_KEY.
  • Chỉ cần GITHUB_TOKEN nếu bạn định quét các repository private. .env.example đi kèm nêu rõ: chỉ token GitHub thì không thể chạy quá trình quét.

Cài Docker và Node 20 trước

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER

Đăng xuất rồi đăng nhập lại để áp dụng membership nhóm mới, sau đó xác nhận Compose plugin đã có.

docker compose version

Chuỗi phiên bản cho biết Compose đã được cài dưới dạng plugin. docker: 'compose' is not a docker command nghĩa là bạn đang dùng binary docker-compose standalone cũ thay vào đó, còn open-kritt gọi docker compose. Thành viên của nhóm docker có quyền tương đương root trên host, vì vậy chỉ thêm account chạy open-kritt vào nhóm này. Xem hướng dẫn đầy đủ hơn tại chạy Docker trên VPS.

Ubuntu 24.04 có Node 18 trong repository riêng, còn CLI sẽ thoát nếu phiên bản thấp hơn 20. Hãy dùng NodeSource.

curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v

node -v phải in ra v20. hoặc cao hơn. Trên Rocky Linux 9, lệnh tương đương là sudo dnf module enable nodejs:20 -y rồi đến sudo dnf install -y nodejs.

Sao chép open-kritt và cố định phiên bản tag

git clone https://github.com/Kritt-ai/open-kritt
cd open-kritt
git fetch --tags
git tag --list
git checkout v1.3.0

main thay đổi theo thời gian. Tag thì không. Tính đến tháng 8 năm 2026, tag mới nhất là v1.3.0, được phát hành vào ngày 4 tháng 8 năm 2026, và git tag --list cho biết trạng thái hiện có vào ngày bạn clone. Checkout một tag sẽ đưa repository vào trạng thái detached HEAD. Điều này là đúng trong trường hợp này: bạn dùng clone này làm deployment được cố định phiên bản, không phải một branch để commit. Khi muốn nâng cấp, hãy đọc release notes, sau đó chạy git fetch --tags, checkout tag mới và chạy lại ./kritt start, vì start sẽ build lại các image.

Không chạy ./kritt cùng với sudo. Tài liệu nêu rõ điều này. CLI quản lý các thư mục credential riêng của project tại .data/, nên nếu chạy bằng root, các thư mục đó sẽ thuộc sở hữu của root và lần chạy bình thường tiếp theo sẽ không thể ghi vào chúng.

Cấu hình quyền truy cập model bằng ./kritt setup

./kritt setup

Lệnh này tạo .env từ .env.example nếu file chưa tồn tại, hiển thị trạng thái của từng credential và cho phép bạn thiết lập hoặc xóa chúng. Lệnh không bao giờ in các giá trị ra terminal. Cả .env và file credential của engine đều được ghi với mode 0600.

Nếu muốn thực hiện thủ công:

cp .env.example .env
chmod 600 .env
mkdir -p .data/codex
chmod 700 .data/codex

Sau đó chỉnh provider key vào .env và giữ file ở mode 0600. Dù dùng cách nào, một provider credential đang hoạt động hiện đã nằm trên server đó. Đây là thêm một lý do để server này không chứa dữ liệu nào khác. Hãy tạo một key chỉ dành cho project này, để sau này việc revoke key không làm hỏng thứ gì bạn cần. Giữ secret ngoài tầm với của AI agent trình bày thói quen rộng hơn này.

Đặt giới hạn chi tiêu của provider trước lần scan đầu tiên

open-kritt được thiết kế để chạy nhiều tác vụ song song, và chính việc chạy song song này tạo ra chi phí. Các giá trị mặc định trong .env.example ở v1.3.0 khá thận trọng: ENGINE_WORKER_COUNT=2, được mô tả trong file là giá trị mặc định thận trọng cho một máy 2 vCPU nhỏ, và ENGINE_MAX_CONCURRENT_SCANS=1. Cao hơn các giá trị này là ENGINE_WORKERS_PER_ACCOUNT=15, số lần gọi root model đồng thời tối đa được phép trên một tài khoản provider, và ENGINE_CODEX_MAX_SUBAGENTS_PER_SESSION=5, vì một phiên Codex có thể chạy tối đa năm child agent. Khi tăng số worker trên một VPS lớn hơn, số lần gọi model đang chạy đồng thời cũng tăng theo.

Repository không có cơ chế giới hạn số tiền bạn chi. .env.example không có thiết lập ngân sách. Các điều kiện dừng riêng của engine chỉ là những giới hạn worker đó cùng với ENGINE_HARNESS_TIMEOUT_SECONDS, mặc định là 7200 giây cho mỗi lần chạy harness. Vì vậy, giới hạn chi tiêu phải được đặt ở provider. Mở console của provider và đặt giới hạn cứng theo tháng trước lần scan đầu tiên, không phải sau đó. Kiểm soát chi phí của một AI agent trên VPS hướng dẫn các thiết lập theo từng provider.

Bạn cũng có thể dùng một cơ chế dừng cục bộ. Đặt ENGINE_WORKER_COUNT=0 sẽ tạm dừng việc nhận job mới. Bạn có thể thay đổi các giá trị worker tương tự trong màn hình Settings sau khi stack đã chạy.

Hướng dẫn này không nêu giá cho mỗi lần scan vì chi phí phụ thuộc vào kích thước repository, workflow bạn xây dựng và model đứng phía sau. Hãy chạy một lần scan trên một repository nhỏ, sau đó xem trang usage của provider trước khi chạy trên bất kỳ repository lớn nào.

Khởi động stack và kiểm tra trạng thái hoạt động

./kritt start

Lệnh này kiểm tra .env và ít nhất một credential, sau đó chạy docker compose up --build. Lần build đầu tiên sẽ chậm vì lệnh này build image cho frontend, backend, engine, executor view và database. Lệnh cũng chạy ở foreground, nên đóng phiên SSH sẽ dừng stack. Hãy chạy stack bên trong tmux, hoặc khởi động ở chế độ detached sau khi lần build đầu tiên thành công. Cả hai cách này đều không tự duy trì sau khi reboot. Nếu muốn stack tự khởi động lại sau khi máy chủ restart, mẫu systemd unit trong duy trì agent tự host chạy qua các lần reboot có thể dùng trực tiếp.

docker compose up -d --build
docker compose ps

docker compose ps phải liệt kê open-kritt-frontend, open-kritt-backend, open-kritt-engine, open-kritt-executor-viewopen-kritt-db. Sau đó kiểm tra backend có trả lời ngay trên máy chủ hay không.

curl -s http://127.0.0.1:3002/api/health

Phản hồi JSON cho biết backend đang hoạt động. Failed to connect to 127.0.0.1 port 3002: Connection refused nghĩa là backend không hoạt động, còn docker compose logs backend sẽ cho biết nguyên nhân. Dừng mọi thứ bằng docker compose down từ thư mục repository.

Một tùy chọn bổ sung: docker compose exec backend npm run seed sẽ nạp dữ liệu demo. Đây là cách nhanh để xem giao diện trước khi chi tiền cho một lần scan thực tế.

Truy cập UI tại cổng 5173 qua SSH tunnel

Mọi service trong file compose mặc định bind vào 127.0.0.1: frontend tại 5173, backend tại 3002, executor view tại 8090 và Postgres tại 5432. Giữ nguyên các binding này và forward cổng qua SSH từ máy của bạn.

ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip

Mở http://localhost:5173 trong trình duyệt cục bộ khi command đó đang chạy. -N nghĩa là kết nối mang theo port forward và không mở shell. Thêm -L 8090:127.0.0.1:8090 thứ hai vào cùng command khi bạn muốn truy cập executor view.

Bạn có thể muốn đặt FRONTEND_BIND_ADDRESS=0.0.0.0 để bỏ qua tunnel. Không nên làm vậy. Backend không có màn hình đăng nhập, nên bất kỳ ai truy cập được trang đó đều có thể khởi chạy scan và tiêu credit của nhà cung cấp. Còn một bẫy khác: cổng container được publish sẽ được xử lý trước khi policy mặc định của ufw áp dụng, nên một rule ufw deny 5173 có vẻ đúng nhưng thực tế không chặn được gì. Các cổng Docker bypass ufw mô tả chuỗi rule gây ra vấn đề này.

Kích thước VPS

ENGINE_MIN_FREE_STORAGE_GB mặc định là 20. Engine sẽ không khởi động container scan mới cho từng job khi dung lượng lưu trữ còn trống thấp hơn mức này. Các image đã build, checkout cache, dữ liệu Postgres và workspace của job đều nằm trên cùng một disk. Vì vậy, VPS 20 GB sẽ không bao giờ chạy được scan. Hãy xem 40 GB là mức tối thiểu và cấp nhiều hơn nếu bạn scan các repository lớn.

Bộ nhớ được tính theo công thức đơn giản. ENGINE_MEMORY_RESERVE_GB=2 giữ lại một phần bộ nhớ cho engine, database, API và phần overhead ngắn hạn. Mỗi scan runner có mức reservation và hard cap là ENGINE_SCAN_RUNNER_MEMORY_MB=1536. Vì vậy, 2 worker cần khoảng 5 GB trước khi chạy bất kỳ thành phần nào khác. Engine chỉ cho phép chạy các runner nằm trong ngân sách bộ nhớ còn lại. Trên máy cấu hình nhỏ, các scan sẽ xếp hàng thay vì bị lỗi. Đây là cách xử lý tốt hơn nhiều so với việc bị out-of-memory killer dừng.

Hai thiết lập prune mặc định là true: ENGINE_AUTO_PRUNE_DOCKER_BUILD_CACHEENGINE_AUTO_PRUNE_UNUSED_DOCKER_IMAGES. Sau khi task hoàn tất, engine xóa build cache không còn dùng, các image không còn dùng và các container scan đã dừng. Các image được container đang chạy tham chiếu, bind mount, dữ liệu database, credential và volume vẫn được giữ lại. Đây cũng là một lý do nữa để không dùng chung host: một pruner mà bạn không cấu hình đang chạy trên Docker daemon đó.

Các thiết lập engine mà hầu hết mọi người cuối cùng đều thay đổi
  • ENGINE_WORKER_COUNT: tổng số worker slot được dùng chung cho các bước scan và post-processing. Đặt thành 0 để tạm dừng nhận job mới.
  • ENGINE_MAX_CONCURRENT_SCANS: số scan được cho phép chạy đồng thời. Các scan đang xếp hàng sẽ chờ đến khi pool đang hoạt động trống.
  • ENGINE_MAX_WORKERS_PER_SCAN: giá trị 0 chia đều tổng số slot cho các scan.
  • ENGINE_HARNESS_TIMEOUT_SECONDS: mặc định là 7200. Đây là thời gian tối đa một job chạy vô hạn có thể tiếp tục.
  • ENGINE_MIN_FREE_STORAGE_GB: mức dung lượng lưu trữ tối thiểu. ENGINE_IGNORE_LOW_STORAGE=true tắt cơ chế bảo vệ này, và file cảnh báo rằng disk của host có thể bị lấp đầy.
  • ENGINE_SCAN_RUNNER_MEMORY_MB: hard cap bộ nhớ cho mỗi runner. Giá trị 0 sẽ bỏ giới hạn này.

Quét repository cục bộ mà không làm lộ dữ liệu

LOCAL_REPOS_PATH mặc định là ./local_repos và được bind mount vào các container backend và engine tại /local_repos. Vì vậy, repository bạn đặt vào thư mục đó trên host sẽ xuất hiện ngay bên trong các container. Hãy dùng một bản clone mới, không dùng working tree đang làm việc. Container chạy job có bản sao được phép ghi, có quyền root bên trong container và được phép truy cập Internet outbound. Điều này có nghĩa là mọi thứ trong bản sao đó đều có thể bị thay đổi hoặc gửi ra ngoài máy chủ. Hãy xóa các file .env và private key trước khi sao chép project vào.

Kết quả nhận được và những gì không nhận được

Bạn nhận được danh sách các phát hiện ứng viên đã được xếp hạng. Bạn không nhận được các lỗ hổng đã được xác minh. Việc xếp hạng và loại trùng quyết định thứ tự trong hàng đợi triage. Chúng không chứng minh một mục là phát hiện thật. Post-script có thể thử xác thực và tạo proof of concept. Đây là tín hiệu mạnh nhất mà tool cung cấp. Tuy nhiên, post-script bị lỗi không phải là bằng chứng cho thấy phát hiện đó là giả. Vẫn cần có người đọc từng ứng viên.

Hướng dẫn này không khẳng định open-kritt tìm được bao nhiêu bug thật, vì chúng tôi chưa đo lường điều đó. Bất kỳ ai đưa ra detection rate cho codebase của bạn đều chưa chạy tool này trên codebase của bạn. Trước tiên, hãy scan một repository mà bạn đã hiểu rõ. Những phát hiện mà bạn có thể tự đánh giá là cách calibration ít tốn kém nhất.

Authorization ở đây quan trọng hơn so với hầu hết các tool self-hosted. Các agent compile và execute code, đồng thời truy cập network, nên bước proof-of-concept có thể tác động đến các hệ thống đang chạy. Chỉ trỏ tool vào code thuộc quyền sở hữu của bạn hoặc nằm trong phạm vi bạn được thuê để kiểm thử. Hãy ghi rõ target scope trước khi chạy bất kỳ thứ gì. Nếu bạn cấu hình ANTHROPIC_API_KEY và dùng Claude Code engine, các thói quen sandboxing trong chạy Claude Code an toàn trên VPS cũng áp dụng cho các agent này.

FAQ

Vì sao open-kritt cần VPS riêng?

Vì các agent phân tích của open-kritt chạy với quyền root bên trong các job container dùng một lần, có bản sao code của bạn ở chế độ ghi và quyền truy cập Internet trực tiếp. Ngoài ra, engine service mount Docker socket của host để có thể khởi chạy một container cho mỗi job. Bất kỳ process nào truy cập được socket đó đều có thể khởi động container mount filesystem của host. Vì vậy, hãy coi toàn bộ stack có quyền root trên host của nó. Trên một VPS riêng, đây là đánh đổi có thể chấp nhận được và việc dựng lại máy không làm mất gì. Trên workstation dùng hằng ngày, cách này đặt SSH key và browser profile của bạn trong cùng trust boundary với code bạn đang quét.

Tôi có thể expose port 5173 thay vì dùng SSH tunnel không?

Không nên. Backend được phát hành mà không có application authentication, nên port là lớp bảo vệ duy nhất giữa Internet với kết quả quét và credit của provider. Vì lý do đó, file compose bind mọi service vào 127.0.0.1. Hãy chạy ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip rồi truy cập http://localhost:5173 trên máy local. Rule của ufw không thay thế được cách này, vì Docker port được publish trước khi default policy của ufw được áp dụng.

Làm cách nào để ngăn open-kritt chi tiêu vượt quá kế hoạch?

Hãy đặt hard limit trong console của model provider trước lần quét đầu tiên, vì open-kritt không có budget setting riêng. Giữ nguyên concurrency mặc định được cung cấp trong lần chạy đầu, ENGINE_WORKER_COUNT=2ENGINE_MAX_CONCURRENT_SCANS=1. Lưu ý rằng một provider account mặc định cho phép tối đa 15 lời gọi root model đồng thời, còn một Codex session có thể chạy tối đa five child agent. ENGINE_WORKER_COUNT=0 tạm dừng việc nhận job mới và là cách dừng local nhanh nhất.

Tôi nên checkout version nào?

Hãy checkout một tag, không bao giờ checkout main. git fetch --tags rồi đến git tag --list sẽ hiển thị các version hiện có. v1.3.0, được publish vào ngày 4 August 2026, là bản mới nhất tại thời điểm viết tài liệu này. Pin version giúp lần rebuild sau nhiều tháng tạo ra cùng một stack. Nó cũng biến việc upgrade thành quyết định được đưa ra sau khi đọc release note, thay vì là hệ quả ngẫu nhiên của việc clone vào một ngày khác.

Scan không bao giờ bắt đầu. Tôi nên kiểm tra gì?

Trước tiên, hãy kiểm tra disk còn trống, vì engine sẽ không khởi chạy scan container riêng cho từng job khi storage còn trống thấp hơn ENGINE_MIN_FREE_STORAGE_GB; giá trị mặc định là 20 GB. Tiếp theo, kiểm tra ENGINE_WORKER_COUNT không bằng 0, vì giá trị đó sẽ tạm dừng việc nhận job mới. Sau đó, xác nhận model credential đã được cấu hình thực sự bằng cách chạy ./kritt setup, vì chỉ có GITHUB_TOKEN thì không thể chạy scan. docker compose logs engine cho biết lý do job bị bỏ qua.