clustercode doctor
clustercode doctorDescription
Section titled “Description”Runs a comprehensive health check on this machine’s ClusterCode setup. Checks:
- Authentication — valid credentials in
~/.clustercode/credentials.json - Worker registration — worker configured with a tenant
- Worker binary — the worker runtime is present (downloaded automatically on first run)
- Orchestrator connectivity — can reach the health endpoint
- WSL2 — (Windows only) WSL2 installed with a Linux distro
- container runtime — Podman or Docker installed and running
- Runtime memory — memory the container runtime can actually see, against host RAM and how many DevBoxes that fits (see below)
- Host memory — memory the host itself still has available, against the floor it needs to stay responsive; warns when the container runtime is holding more than the host can spare (see below)
- Disk space — free space on the drive that holds
~/.clustercode; warns below 20 GB - Runtime disk — (Windows, Podman on WSL2) disk space the container runtime holds on your drive but no longer uses (see below)
- Memory — installed RAM (total, not free); warns below 8 GB
Options
Section titled “Options”| Flag | Description |
|---|---|
--json | Machine-readable JSON output (for CI/automation) |
Output
Section titled “Output”Each check shows a pass/fail/warn status:
✓ Logged in as you@example.com ✓ Registered (your-tenant) ✓ Reachable (console.clustercode.io) ✓ podman v5.3.1 ✓ Runtime memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes ✓ Host memory: 18.3 GiB available of 31.6 GiB ✓ Disk: 42.0GB free on C: ⚠ Runtime disk: 70.8 GB on host, 43.0 GB used inside — ~27.8 GB reclaimable (C: 42.0 GB free); run `clustercode machine compact` ✓ RAM: 31.6GB totalIcons: ✓ pass, ✗ fail, ⚠ warning. Disk space, runtime disk and memory use warnings (⚠) instead of failures — they won’t block you but indicate potential issues.
Runtime memory
Section titled “Runtime memory”On macOS and Windows, Podman and Docker run inside a VM that only sees a
fraction of the host’s RAM, and that VM’s memory is what limits how many
DevBoxes this worker can run — not the host’s total. This check reports the
engine-visible number against host RAM and an estimated DevBox capacity.
Values are in GiB in the output, computed from the underlying MiB (1024-based)
units — see RUNTIME_MEMORY_MB.
A passing reading with headroom to spare looks like this:
Runtime memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxesDevBox counts are estimates at the default size — actual capacity depends on the sizes you launch. When the engine is below what’s recommended for a shared machine and nothing has been deliberately configured, the check warns instead and suggests a value:
Runtime memory: 8.0 GiB of 31.6 GiB host — fits ~1 default (4 GiB) DevBox — more host memory is available; raise with `clustercode onboard --memory 25600`Once the engine is at or above that shared-machine recommendation — but still below what a dedicated worker could use — the check passes, but the line still grows a nudge rather than staying silent:
Runtime memory: 16.0 GiB of 31.6 GiB host — fits ~3 default (4 GiB) DevBoxes (dedicated worker? up to 25.0 GiB — see `clustercode onboard`)Memory reclaim (Windows, WSL backend)
Section titled “Memory reclaim (Windows, WSL backend)”A runtime of a perfectly reasonable size is still a problem when nothing ever
returns what it borrows. On Windows the WSL2 VM only hands memory back to the
host while memory reclaim ([experimental] autoMemoryReclaim) is on, so this check reads
.wslconfig and your WSL version and appends what it finds. With Podman, the
fix is a command:
Runtime memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes — memory reclaim is off, so the runtime keeps memory the host may need; run `clustercode onboard` to enable itWith Docker as the engine, onboard doesn’t edit .wslconfig, so the check
names the setting instead:
Docker memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes — memory reclaim is off, so the runtime keeps memory the host may need; set [experimental] autoMemoryReclaim=gradual in .wslconfig, then `wsl --shutdown`On WSL builds older than 2.0 the setting doesn’t exist at all, so the only remedy is a smaller ceiling — and the check names the size the no-reclaim sizing would have recommended in the first place:
Runtime memory: 26.0 GiB of 31.6 GiB host — fits ~6 default (4 GiB) DevBoxes — memory reclaim needs WSL 2.0 or newer, so the runtime keeps everything it touches; lower to `clustercode onboard --memory 23552`It won’t tell a runtime that’s already at or below that ceiling to “lower” itself to a bigger number — in that case you just get the explanation. Both forms drop the check to a warning.
Writing the setting isn’t the same as the VM honouring it, so the check distinguishes configured from measured. With reclaim on (set by hand, or WSL’s default) but never verified, and the runtime sized as though it works, that’s a warning with two ways out — measure it, or size for the truth you currently have:
Runtime memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes — memory reclaim is configured but unverified, and the runtime is sized as if it works; verify with `clustercode onboard --verify-reclaim` or lower to `clustercode onboard --memory 23552`If the runtime is already within what a no-reclaim host can afford, nothing is wrong — the check passes and just notes the open question:
Runtime memory: 16.0 GiB of 31.6 GiB host — fits ~3 default (4 GiB) DevBoxes (memory reclaim configured but unverified — `clustercode onboard --verify-reclaim`)A verdict applies only to the WSL build and reclaim mode it was measured under,
so after a WSL upgrade or a switch between gradual and dropcache you’re
back to one of these two lines until you measure again.
The exception is Podman in dropcache mode on WSL older than 2.9.8, including
a .wslconfig that doesn’t set the key. The CLI
can’t measure that, so
neither line offers the measurement:
Runtime memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes — memory reclaim is configured but cannot be verified on this WSL version, so size as if it does not work; lower to `clustercode onboard --memory 23552`Runtime memory: 16.0 GiB of 31.6 GiB host — fits ~3 default (4 GiB) DevBoxes (memory reclaim configured but not verifiable on this WSL version)If wsl --version can’t be read, the check warns that it couldn’t tell, and
names the size to lower to when the runtime is above the no-reclaim ceiling:
Runtime memory: 26.0 GiB of 31.6 GiB host — fits ~6 default (4 GiB) DevBoxes — could not read the WSL version (`wsl --version`), so memory reclaim cannot be counted on; lower to `clustercode onboard --memory 23552`Docker never gets past configured: the CLI can’t measure Docker’s VM, so it offers no measurement and sizes Docker as if reclaim does not work. Above the no-reclaim ceiling that’s a warning naming Docker’s own setting:
Docker memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes — memory reclaim is configured but cannot be verified for Docker, so size as if it does not work; lower [wsl2] memory= to 23552MBand at or below it, a passing note:
Docker memory: 16.0 GiB of 31.6 GiB host — fits ~3 default (4 GiB) DevBoxes (memory reclaim configured but not verifiable for Docker)Once it has been measured and found to do nothing on this build, no amount of configuration will change that, so the only lever left is the size:
Runtime memory: 25.0 GiB of 31.6 GiB host — fits ~5 default (4 GiB) DevBoxes — memory reclaim does not return memory on this Windows build; lower to `clustercode onboard --memory 23552`and, again, a runtime already sized for that reality passes with a note instead:
Runtime memory: 16.0 GiB of 31.6 GiB host — fits ~3 default (4 GiB) DevBoxes (memory reclaim inert on this build)Measured and working is the one status that buys the smaller reserve — which may be news to a runtime that was sized conservatively before anyone knew:
Runtime memory: 16.0 GiB of 31.6 GiB host — fits ~3 default (4 GiB) DevBoxes (memory reclaim verified — up to 25.0 GiB; see `clustercode onboard`)The rule underneath all of these: only a verified status changes the
sizing. Off, unsupported, version unknown, configured-but-unverified, unverifiable and inert are all graded
the same way — as a VM that keeps what it takes. None of these lines is ever
shown for a Hyper-V backend, which doesn’t read .wslconfig, or on macOS and
Linux, which have no such setting.
At most one reclaim note appears, and it takes the place of the “dedicated
worker?” nudge rather than joining it — doctor prints one line per check, and
two suggestions on one line is neither.
With Docker as the engine, every “lower to clustercode onboard --memory N”
above reads lower [wsl2] memory= to NMB instead, because onboard won’t edit
that file for you.
See Verifying it works on your machine for what the measurement actually does, and for recording a verdict you took by hand.
This check never fails — an undersized runtime just caps how much work the worker takes on, it doesn’t break it. It warns when the engine is too small to fit even one default-sized DevBox, when it’s below what’s recommended for a shared machine and nothing was deliberately configured, or when the runtime is sized larger than a host without working memory reclaim can afford. On Linux, Podman runs containers directly with no VM in between, so this reports host memory directly. If the runtime is stopped, it reports “unknown” rather than guessing.
On Windows with Docker, this check only reports the number — doctor (and
onboard) never resize Docker’s memory for you. The setting lives in
[wsl2] memory= in .wslconfig (Docker Desktop’s WSL2 backend) or in Docker
Desktop’s own settings (its Hyper-V backend). The
[experimental] autoMemoryReclaim entry is likewise yours to set by hand on
the WSL2 backend — doctor reports whether it’s on, but onboard won’t
write it, and nothing can verify it for Docker, so Docker is always sized as if
reclaim does not work. The lever that protects the host is a smaller
[wsl2] memory= (WSL2 backend) or a smaller memory setting in Docker Desktop
(Hyper-V backend). See
Sizing container runtime memory for how to
change either.
Host memory
Section titled “Host memory”Every other memory figure doctor prints is a plan: a ceiling written into a
config file, a reserve subtracted once at sizing time. This one is a
measurement, and it’s the only check that can tell you the reserve isn’t
being honoured — a VM that never returns what it borrows passes every other
check while the machine pages itself to a standstill.
What it reads, per platform:
| Platform | Reading | Floor the host needs |
|---|---|---|
| Windows | available physical memory | 4 GiB |
| macOS | free + inactive pages, from vm_stat | 6 GiB |
| Linux | MemAvailable | 2 GiB |
(macOS is the one platform where “free memory” is the wrong number — a healthy Mac keeps almost no free pages and hands out inactive ones on demand, so reporting free pages alone would make every Mac look permanently starved.)
Above the floor, the line is just the two numbers:
Host memory: 18.3 GiB available of 31.6 GiBBelow it, the check warns and names the floor. When a running container runtime is sized above what a host without memory reclaim can afford, it also names the value to shrink to:
Host memory: 2.0 GiB available of 31.6 GiB — below the 4.0 GiB the host needs; the container runtime is holding memory the host cannot spare — lower it with `clustercode onboard --memory 23552`The action at the end names whatever actually sets that runtime’s size, the same way the runtime-memory line above it does:
| Runtime | Action |
|---|---|
Podman that onboard can resize | lower it with `clustercode onboard --memory 23552` |
| Podman whose machine type couldn’t be determined | lower the Podman machine's memory to 23552MB |
| Docker on the WSL2 backend | lower [wsl2] memory= to 23552MB |
| Docker on the Hyper-V backend, or on macOS | lower it to 23552MB in Docker Desktop settings |
| Docker on Windows, backend unknown | lower [wsl2] memory= to 23552MB (WSL2 backend), or lower it to 23552MB in Docker Desktop settings (Hyper-V backend) |
onboard never resizes Docker, so Docker is never sent there.
When the runtime is already at or below that ceiling, there’s nothing to shrink, so the line reports the reclaim state instead:
Host memory: 3.1 GiB available of 31.6 GiB — below the 4.0 GiB the host needs; memory reclaim is off — see the docsThe tail of that line names which reclaim state you’re in, because they’re different diagnoses with different next steps:
| Tail | What it means |
|---|---|
memory reclaim is off — see the docs | autoMemoryReclaim=disabled, or unset or unrecognised on WSL older than 2.1.3, or a .wslconfig the CLI can’t read as UTF-8 |
memory reclaim needs WSL 2.0 or newer — see the docs | This WSL build predates the setting |
memory reclaim cannot be checked, because the WSL version could not be read — see the docs | wsl --version can’t be read |
memory reclaim is configured but unverified — see the docs | Reclaim is on (set by hand, or WSL’s default); nobody has measured it (as far as Docker ever gets) |
memory reclaim is configured but cannot be verified on this WSL version — see the docs | Podman in dropcache mode on WSL older than 2.9.8, which the CLI can’t measure |
memory reclaim is on but inert on this build — see the docs | Measured, and it returns nothing here |
memory reclaim is on but not keeping up — see the docs | Measured and working, simply outpaced |
Where there is no reclaim setting at all — macOS, or a Hyper-V backend — the line leaves the reclaim clause out entirely and just reports the shortfall, rather than pointing you at a setting that doesn’t exist.
If the container runtime isn’t running, it isn’t the cause of the pressure, so no runtime advice is offered at all — and on Linux there is no VM holding memory apart from the host, so the warning carries no runtime instructions there either.
This check never fails, whatever the reading: a host under memory pressure
is a machine still doing its job, and doctor’s exit code gates scripted
setup. The reading is also instantaneous — it can dip during a build or an
image pull and recover a moment later. Act on it when it persists, not on a
single sample.
Runtime disk (Windows)
Section titled “Runtime disk (Windows)”On Windows, the Podman machine keeps everything — images, DevBoxes, build
caches, logs — in one virtual disk file on your drive. That file grows when
the machine needs room and never shrinks when files are deleted inside
it, so space freed inside the machine stays taken on your drive. This check
compares the file’s size on your drive with the space actually used inside
the machine; the difference is what
clustercode machine compact can give back.
It only appears on Windows with a WSL2-backed Podman machine. On macOS, Linux, Docker Desktop, or a Hyper-V machine there is no Runtime disk line at all.
It measures the default Podman machine, or the first one listed when none is
set as the default. When you have several machines, the line names the one it
measured: Runtime disk (machine dev): ….
Severity is relative to how much free space you have, because the same amount of dead space matters on a nearly full drive and not on a roomy one. The check warns when the reclaimable space is at least 5 GB and at least 25% of the free space on the drive that holds the disk file, and passes otherwise. Twenty gigabytes of dead space with 30 GB free warns; the same 20 GB with 500 GB free passes.
Runtime disk: 44.2 GB on host, 43.0 GB used inside — ~1.2 GB reclaimable (C: 58.6 GB free)When it warns, the line names the command to run:
Runtime disk: 70.8 GB on host, 43.0 GB used inside — ~27.8 GB reclaimable (C: 32.0 GB free); run `clustercode machine compact`If the machine also holds stopped containers (its user’s or root’s), the
warning adds
; stopped containers also hold space; stopped DevBoxes can be cleaned up in the console
— compacting doesn’t return their space.
Sizes are labelled GB but computed in 1024-based units, the same as the Disk space line.
“Used inside” is more than the size of the machine’s files. The machine’s ext4
filesystem also keeps space for itself — its journal, and the inode-table
entries of the files in use — and that space is written to the disk file, so no
compact returns it. The check reads the filesystem’s size information
read-only (with sudo -n, which never asks for a password) and adds it to the
files’ size. If it can’t read that, “used inside” counts the files alone, and
the reclaimable figure comes out somewhat higher than what a compact can give
back.
The reclaimable figure is an estimate either way. The disk file grows and shrinks in 1 MiB blocks, and a block that is only partly used stays in the file after a compact, so a freshly compacted disk can still show a GB or two reclaimable. The figure never goes below zero.
The size used inside can only be measured while the machine is running. If it’s stopped, the check warns and asks you to start the container runtime rather than guessing; if it’s running but doesn’t answer within 30 seconds, the check warns that it could not measure usage inside the machine. If your drive’s free space can’t be read within 15 seconds, the free space is left off the line and the check warns whenever at least 5 GB could be reclaimed.
This check never fails: dead space is wasteful, not broken, and doctor’s
exit code gates scripted setup. It is read-only — it never stops, trims, or
compacts anything. In doctor --json it appears in checks with the name
runtime-disk; its detail contains sizes and a drive letter, never the disk
file’s path or your user name.
If the line keeps coming back, see C: keeps filling up.
Interactive Fix
Section titled “Interactive Fix”When failures are found, doctor prompts you to run clustercode onboard to fix them automatically. If only warnings remain, it reports them without prompting.
Run doctor, then connect
Section titled “Run doctor, then connect”To check, fix and bring the worker online in one step, run doctor through the worker command:
clustercode connect --doctorDoctor runs first, exactly as above; once it finishes, the worker connects. Issues that are still unresolved are shown as a warning rather than stopping the connection. See clustercode worker for details.
CI Usage
Section titled “CI Usage”clustercode doctor --jsonReturns a JSON object with healthy (boolean) and checks (array). Exit code is 0 when healthy, 1 otherwise.