How to self-host open-kritt for VPS scanning
Run open-kritt on VPS with Docker Compose, pin a release, tunnel UI port 5173 through SSH, and set your provider budget before scanning.
Why you suppose self-host open-kritt for VPS and no be your laptop
Self-host open-kritt for server wey you fit destroy and rebuild. The tool dey run its analysis agents as root inside disposable job containers, give each one writable copy of your code and direct internet access, and mount the host Docker socket inside its engine service. This trade-off fit make sense for machine wey na only for this job. But e bad for machine wey dey hold your SSH keys.
Four properties of the default setup dey support this advice, and all four come from the project own README and compose file.
The agents suppose get strong permissions. The README talk say tool-enabled agents dey run as root inside disposable job containers, with writable repository copies and direct internet access, so dem fit install tools, compile targets, run tests, and build proofs of concept. Scan no be just linter wey dey read files. Na arbitrary code execution wey you request. That internet access fit work for both sides: anything wey agent fetch while e dey research target na untrusted text wey enter its prompt, just like the exposure wey you accept when you give agent its own web search.
The engine get the Docker socket. docker-compose.yml mounts the host Docker socket inside the engine service, because the engine dey build and launch one scan container for each job. Any process wey fit reach that socket fit start container wey mounts the host filesystem. So, the engine effectively get root-level control of any host wey dey run am.
No login screen dey. The backend ships without application authentication. Access to the port means access to your findings and your provider credit.
The code wey you scan often no be your own. If you point agents to third-party repository, you dey run that repository build for your machine, as root, with network access.
If you don read why coding agents suppose dey inside disposable VM, na the same threat model be this one, only say e stronger. Give open-kritt one VPS wey nothing else dey run on, and control that VPS from a separate least-privilege user account instead of root.
Wetin open-kritt really dey do
open-kritt (the repository na Kritt-ai/open-kritt, and e get AGPL-3.0 license) dey break vulnerability research into small tasks. E run the tasks across AI agents in parallel, then e remove duplicates and rank the results. You define workflow as chain of focused prompts, and each step dey receive structured context from the steps before am. The scan target fit be remote or local git repository. The analysis engine na Codex or Claude Code. After candidate show, optional post-scripts fit try validate am or build proof of concept.
The final result na ranked list of candidates. Treat am as triage queue, no be report.
Wetín you need before you start
- A VPS wey dey run Ubuntu 24.04, Debian 12 or Rocky Linux 9. The install docs list dem as the distributions wey dem test, for x86_64 and ARM64.
- Docker Engine with the Compose plugin.
- Node.js 20 or newer for the host, because the
./krittCLI dey run for the host instead of inside a container. - One model provider: a Codex login, or
OPENAI_API_KEY,CODEX_API_KEY,ANTHROPIC_API_KEYorOPENROUTER_API_KEY. GITHUB_TOKENonly if you plan scan private repositories. The shipped.env.exampletalk am plainly: GitHub token by itself no fit run scans.
Install Docker and Node 20 first
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USERLog out and back in so the new group membership go apply, then confirm say the Compose plugin dey present.
docker compose versionVersion string mean say Compose install as plugin. docker: 'compose' is not a docker command mean say you get the old standalone docker-compose binary instead, and open-kritt call am docker compose. Membership of the docker group dey equivalent to root for the host, so na only the account wey dey run open-kritt you suppose put inside am. For the longer version of this setup, see how to run Docker for VPS.
Ubuntu 24.04 ship Node 18 for its own repository, and the CLI go exit for anything below 20. Use NodeSource.
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -vnode -v must print v20. or higher. For Rocky Linux 9, the equivalent na sudo dnf module enable nodejs:20 -y followed by sudo dnf install -y nodejs.
Clone open-kritt and lock one tagged release
git clone https://github.com/Kritt-ai/open-kritt
cd open-kritt
git fetch --tags
git tag --list
git checkout v1.3.0main dey change under you. Tag no dey change. As of August 2026, the newest tag na v1.3.0, wey dem publish on 4 August 2026, and git tag --list go show wetin dey available on the day wey you clone. When you check out tag, repository go enter detached HEAD state. This one correct for here because you dey use this clone as locked deployment, no be branch wey you go commit to. If you wan upgrade later, read the release notes, then run git fetch --tags, check out the new tag, and run ./kritt start again, because start dey rebuild the images.
No run ./kritt with sudo. Documentation talk this one clear. The CLI dey manage project-local credential directories under .data/. If you run am as root, those directories go belong to root, and the next normal run no go fit write to dem.
Configure model access with ./kritt setup
./kritt setupThis command go create .env from .env.example if e no dey exist, print the status of each credential, and allow you set or unset dem. E no ever print the values back to the terminal. Both .env and the engine credential file go write with mode 0600.
If you prefer do am by hand:
cp .env.example .env
chmod 600 .env
mkdir -p .data/codex
chmod 700 .data/codexThen edit the provider key enter .env and leave the file with mode 0600. Either way, working provider credential don dey for that server now. Na another reason make the box no hold anything else. Create key for this project alone, so if you revoke am later, e no go break anything wey you care about. Keep secrets away from AI agents explain this wider practice.
Set provider spending cap before first scan
open-kritt dey designed to fan out, and na this fan-out dey cost money. The defaults for .env.example for v1.3.0 dey conservative: ENGINE_WORKER_COUNT=2, wey the file describe as conservative default for small 2-vCPU machine, and ENGINE_MAX_CONCURRENT_SCANS=1. Above dem dey ENGINE_WORKERS_PER_ACCOUNT=15, the maximum number of concurrent root model calls wey one provider account fit allow, and ENGINE_CODEX_MAX_SUBAGENTS_PER_SESSION=5, because one Codex session fit run up to five child agents. If you increase worker count for bigger VPS, the number of model calls wey dey run at the same time go increase too.
Nothing for the repository limit how much you fit spend. No budget setting dey for .env.example. The engine own stop conditions na those worker limits plus ENGINE_HARNESS_TIMEOUT_SECONDS, wey default to 7200 seconds for each harness run. So the provider na where the spending limit suppose dey. Open your provider console and set hard monthly limit before the first scan, no be after e don happen. How to control wetin AI agent dey cost you for VPS explain the settings for each provider.
Local brake dey too. If you set ENGINE_WORKER_COUNT=0, e go pause pickup of new jobs. You fit also change the same worker values for the Settings screen after the stack don start.
This guide no give price for each scan, because the cost depend on repository size, the workflow wey you build, and the model wey dey behind am. Run one scan against one small repository first. Then check your provider usage page before you point am to anything big.
Start stack and check whether e healthy
./kritt startThis one dey check .env plus at least one credential, then e run docker compose up --build. The first build slow because e dey build frontend, backend, engine, executor view, and database images. E dey run for foreground too, so if you close SSH session, stack go stop. Start am inside tmux, or bring am up detached after the first build don succeed. None of these go survive reboot by itself. So, if you want stack to come back after the box restart, the systemd unit pattern for keeping a self-hosted agent running across reboots fit transfer directly.
docker compose up -d --build
docker compose psdocker compose ps suppose list open-kritt-frontend, open-kritt-backend, open-kritt-engine, open-kritt-executor-view, and open-kritt-db. Then check say backend dey answer from the server itself.
curl -s http://127.0.0.1:3002/api/healthJSON response mean say backend dey up. Failed to connect to 127.0.0.1 port 3002: Connection refused mean say e no dey up, and docker compose logs backend go show why. Stop everything with docker compose down from the repository directory.
One optional extra: docker compose exec backend npm run seed dey load demo data. This na cheap way to check the interface before you spend anything for real scan.
Geh UI reach for port 5173 through SSH tunnel
Every service for the compose file dey bind to 127.0.0.1 by default: frontend dey for 5173, backend dey for 3002, executor view dey for 8090 and Postgres dey for 5432. Leave those bindings as dem be, then forward the port over SSH from your own machine.
ssh -N -L 5173:127.0.0.1:5173 you@your-server-ipOpen http://localhost:5173 for your local browser while that command dey run. -N mean say the connection go carry the forward and no shell. Add another -L 8090:127.0.0.1:8090 to the same command when you want executor view too.
You fit want set FRONTEND_BIND_ADDRESS=0.0.0.0 and skip the tunnel. No do am. Backend no get login screen, so anybody wey reach that page fit start scans and spend your provider credit. Another problem dey underneath: published container port dey handled before ufw default policy apply, so ufw deny 5173 rule fit look correct but e no block anything. Docker ports wey bypass ufw show the rule chain wey cause am.
VPS Size
ENGINE_MIN_FREE_STORAGE_GB default na 20, and engine no go start new per-job scan container when free storage drop below am. Built images, checkout cache, Postgres data and job workspaces all dey for the same disk, so 20 GB VPS no go ever start scan. Treat 40 GB as the minimum, and give am more if you dey scan large repositories.
Memory dey follow simple calculation. ENGINE_MEMORY_RESERVE_GB=2 dey reserve memory for engine, database, API and short-lived overhead, while each scan runner get reservation and hard cap of ENGINE_SCAN_RUNNER_MEMORY_MB=1536. Two workers therefore need about 5 GB before anything else run. Engine go admit only runners wey fit inside the remaining budget, so scans go queue for small server instead of failing. This failure mode better pass out-of-memory killer.
Two prune settings default to true: ENGINE_AUTO_PRUNE_DOCKER_BUILD_CACHE and ENGINE_AUTO_PRUNE_UNUSED_DOCKER_IMAGES. After task complete, engine dey remove unused build cache, unused images and stopped scan containers. Images wey running container dey reference, bind mounts, database data, credentials and volumes dey remain. This na another reason not to share the host: pruner wey you no configure dey run against that Docker daemon.
Engine settings wey most people dey eventually change
ENGINE_WORKER_COUNT: total worker slots wey scan steps and post-processing share. Set am to 0 to pause pickup of new jobs.ENGINE_MAX_CONCURRENT_SCANS: how many scans engine fit admit at once. Queued scans go wait until active pool empty.ENGINE_MAX_WORKERS_PER_SCAN: 0 go share the aggregate slots evenly among scans.ENGINE_HARNESS_TIMEOUT_SECONDS: 7200 by default. This na the longest time one runaway job fit run.ENGINE_MIN_FREE_STORAGE_GB: storage minimum.ENGINE_IGNORE_LOW_STORAGE=truedey disable the safeguard, and the file warns say this fit fill the host disk.ENGINE_SCAN_RUNNER_MEMORY_MB: hard memory cap for each runner. 0 go remove the cap.
Scanning a local repository without leaking it
LOCAL_REPOS_PATH default na ./local_repos, and system dey bind-mount am into backend and engine containers for /local_repos. So, repository wey you put for that folder on host go show inside the containers immediately. Use fresh clone, no be your working tree. Job container get writable copy, root access inside itself, and outbound internet access. This mean say anything wey dey inside that copy fit change or send comot from the server. Remove .env files and private keys before you copy project enter.
Wetín you go get, and wetín you no go get
You go get ranked candidate findings. You no go get verified vulnerabilities. Ranking and de-duplication decide the order of your triage queue. Dem no prove say any entry na real. Post-scripts fit try validation and build proof of concept, and this na the strongest signal wey the tool fit offer, but if post-script fail, e no mean say the finding false. Person still need read every candidate.
This guide no claim how many real bugs open-kritt finds, because we never measure am. Anybody wey quote detection rate for your codebase never run am against your codebase. Scan repository wey you already know well first: findings wey you fit judge by yourself na the cheapest calibration wey dey available.
Authorization matter pass for here compared with most self-hosted tools. The agents compile and execute code and reach the network, so proof-of-concept step fit touch live systems. Point am to code wey you own or get contract to test, and write down the target scope before you run anything. If you configure ANTHROPIC_API_KEY and use the Claude Code engine, the sandboxing habits for running Claude Code safely on a VPS apply to these agents too.
FAQ
Why open-kritt need e own VPS?
Because e analysis agents dey run as root inside disposable job containers wey get writable copies of your code and direct internet access. The engine service also mounts the host Docker socket so e fit launch one container for each job. Any process wey fit reach that socket fit start a container wey mounts the host filesystem. So, treat the whole stack like root for the host. For dedicated VPS, this trade-off dey acceptable, and rebuilding the box no cost you anything. For your daily workstation, e puts your SSH keys and browser profiles inside the same trust boundary with the code wey you dey scan.
I fit expose port 5173 instead of using an SSH tunnel?
You no suppose do am. The backend ships without application authentication, so na the port dey between the internet and your findings plus provider credit. The compose file binds every service to 127.0.0.1 for this reason. Run ssh -N -L 5173:127.0.0.1:5173 you@your-server-ip and open http://localhost:5173 locally instead. A ufw rule no be substitute, because published Docker port dey handled before ufw default policy apply.
How I fit stop open-kritt from spending pass wetin I plan?
Set hard limit for your model provider console before the first scan, because open-kritt no get budget setting of its own. Keep the shipped concurrency defaults for the first few runs, ENGINE_WORKER_COUNT=2 and ENGINE_MAX_CONCURRENT_SCANS=1, and remember say one provider account allows up to 15 concurrent root model calls by default, while one Codex session fit run up to five child agents. ENGINE_WORKER_COUNT=0 pauses pickup of new jobs and na the fastest local stop.
Which version I suppose check out?
A tag, never main. git fetch --tags followed by git tag --list shows wetin dey available, and v1.3.0, published on 4 August 2026, na the newest as of this writing. Pinning mean say rebuild months later go produce the same stack. E also make upgrading a decision wey you take after reading release notes, instead of side effect of cloning on another day.
A scan no dey ever start. Wetin I suppose check?
First check free disk, because the engine no go launch per-job scan container when free storage dey below ENGINE_MIN_FREE_STORAGE_GB, wey default to 20 GB. Then check say ENGINE_WORKER_COUNT no be 0, because that value pauses pickup of new jobs. Then confirm say model credential really dey configured by running ./kritt setup, because GITHUB_TOKEN by itself no fit run scans. docker compose logs engine names the reason e skip the job.