Measure the circle
$ grim diagnose
"It is slow on my machine" is a sentence, not a measurement. grim diagnose turns it into numbers in under a minute: what the machine is, which Docker and which file sharing it runs, how many files the bind mount carries, how long the first byte of / and /admin takes, whether opcache is on inside the container. The numbers are printed, kept in the project under .grim/, and with --send they land on the portal next to the numbers of everybody else who runs the same project — with a baseline to read them against.
grim doctor asks whether the tools are there. grim diagnose asks how fast the project is with them.
Usage
$ grim diagnose # the quick set, a table, under a minute
$ grim diagnose --send # the same, posted to the portal
$ grim diagnose --full # plus a cold and a warm up, the image build, composer, vite, the suite
$ grim diagnose --json # the payload instead of the tableRun it in the project directory with the stack up (grim up). Without a running stack the TTFB and opcache rows say so and the rest is still measured.
Arguments and options
Measure the machine and the running project: Docker, file sharing, bind mount size, TTFB, opcache — and send it to the portal on request
Usage
grim diagnose [--full] [--send] [--json]Options
| Option | Description |
|---|---|
--full |
Also time a cold and a warm up, the image build, composer install, the Vite build and the suite (minutes, restarts the stack) |
--send |
Post the run to the portal (Diagnostics tab of the project) with your GitHub token |
--json |
Print the payload instead of the table |
What it actually does
- Reads the machine. OS, CPU cores, RAM, free disk under the project, the Docker server version, and on macOS the file sharing mode of Docker Desktop (
virtiofs,grpc-fuse,osxfs) — read from/proc/mountsinside the app container, where the project's bind mount names its type (fakeowneris VirtioFS behind Docker Desktop's ownership shim); with no container running, Docker Desktop's settings file is the fallback, and it names only what differs from the defaults. Linux binds natively. - Counts the bind mount. Files under the project without
vendor,node_modulesand.git, and again with them — the second number is what Docker Desktop has to keep in sync. - Times the first byte. Five warm requests each to
https://<project>.test/and/admin(a customtls.domainis honoured), the median in milliseconds. Not the mean: one slow request must not move the number. Only a 2xx or 3xx answer counts — Traefik's 404 while the stack is down is fast, and it is not the app. - Asks the container.
php -rinsideappfor the PHP version and opcache status, including the hit rate. - With
--full, times the slow things, each as an optional step:down+upcold, a restart warm,docker compose build,composer install,npx vite build, and the suite through the same runnergrim testuses. Minutes, and the stack is restarted. - Writes
.grim/diagnose-<timestamp>.json— schema version 1, the project name, the versions of grim, core and the train, the metrics. The machine is a short hash of the hostname and grim's install id; no hostname, no paths, no.envvalues are in the file. - With
--send, posts that file to the portal with your GitHub token — the same door the deploy heartbeat uses. The run then shows under Registry → Projects → the project → Diagnostics, where a keeper can mark one run as the baseline; cells slower than twice the baseline are red.
Pitfalls
TTFB / says "no answer"
Nothing on https://<project>.test/ answered with a 2xx or 3xx. The stack is down (Traefik alone answers 404), or Traefik is not bound to 443 — grim up says which. The rest of the table is still valid.
The portal answers 404
Not sent — this portal does not take diagnose runs yet. The project's registry_url points at a portal older than the Diagnostics tab. The report is still kept locally; send it later or paste the JSON.
The numbers are worse than a colleague's
Compare the File sharing row first. On macOS virtiofs against grpc-fuse is often the whole difference, and it is a switch in Docker Desktop, not in the project — the row says where (Settings → General → Virtual Machine Options → VirtioFS) whenever it reads grpc-fuse or osxfs. Then the Files row: a bind mount with a fat node_modules is slow on every sharing mode — grim tune has the knobs for the container side.
--full restarts the stack and runs the suite; do not run it with uncommitted work in a state you would not want migrated or tested.
After it finishes
Nothing has changed on the machine except a new file under .grim/, which the project's .gitignore already hides. With --send, one more row on the portal.
Related spells
grim doctor
Check your circle
Check every tool grim expects on this machine, and the project you are standing in, and say what to do about what is missing.
grim up
Raise the stack
Start the project's containers and its Vite dev server, with a trusted certificate and a working address, and say where to open it.
grim tune
Size the production stack
Render the PHP-FPM pool, php.ini and nginx config the production stack mounts, from a profile in grim.json or from the CPU and memory of the server itself.
grim test
Run the test suite
Run the project's tests in the app container against a separate test database that grim creates and migrates for you.