Appearance
When something is wrong
Start here:
bash
dqx doctorIt reports what is broken in this project and what to do about it — not a health score, a list of findings with severities and remedies.
What it checks
- The machine: is the backend running, does something else hold 80 and 443, is mkcert's CA installed
- The project: does every declared domain have a route and a certificate, does the docroot exist, is a container restarting in a loop
- The application: can each site reach the schema its
.envnames, isQUEUE_CONNECTIONset to something nothing is running, does the config declare a PHP image the stack does not run - The repository: does the config name a repository the checkout does not point at
Each finding says which of the two possible things is wrong, and neither is guessed at. When a project's .env asks for a database the config does not declare, dqx does not pick a winner — it tells you the two names and offers both ways to make them agree.
The failures that are hardest to see
These are the ones dqx was written for, because none of them announce themselves:
| what you see | what is actually happening |
|---|---|
| The site loads, but nothing saves | The schema exists but the user has no grant on it |
| Everything is 200, jobs never run | QUEUE_CONNECTION is set and nothing is consuming the queue |
| One page is 500, the rest is fine | A worker died and its container is restarting in a loop |
| It worked yesterday | Somebody changed the compose file and not the config, or the reverse |
| A colleague's build differs | The config names one repository and the checkout is at another |
up never finishes, docker calls a healthy service unhealthy | The probe cannot run at all |
| Redis or the database refuses to start on data that was fine | A floating tag is not the same image twice |
Two that are worth their own command
A healthcheck that can never pass. Compose hands a test: written as a string to a shell whole, so the container tries to run [CMD-SHELL, and answers exit 127 — on every probe, for ever. The service behind it is usually fine; one project's Elasticsearch had recovered 62 indices and was serving while docker called it unhealthy. What you see is up refusing to call the project started.
bash
dqx healthchecks # what cannot run here
dqx healthchecks --write # fix it: the check becomes a YAML list
dqx healthchecks --all ~/dockers # across every project under a directoryMeasured on one fleet the first time this ran: 35 such checks in 23 of 32 projects.
An image tag that names no version. redis:alpine is whatever the engine you are on happened to pull last, so the same compose file is a different image on a colleague's laptop and on CI, which pulls fresh every run. For a service that keeps data on disk that is a format mismatch waiting:
# Can't handle RDB format version 15
# Fatal error loading the DB, check server logs. Exiting.The data was fine — the reader was older. The message names neither the tag nor the version.
bash
dqx images # data-owning services on a floating tag, and the version behind it
dqx images --write # pin them to the minor series
dqx images --outdated # which pins have fallen behind upstreamOnly services that own data are reported. The mail catcher keeps nothing a version can be wrong about, so it is left to follow upstream.
A pin is a promise that somebody raises it later, which is what --outdated is for. Raising one is a decision rather than a chore: read what changed in the data format first.
Keeping the house images fresh
bash
dqx update-images # what this machine already has, brought up to date
dqx update-images --dry-run # say what would be pulled, pull nothing
dqx update-images --major # also cross a major versionOnly what is already here. A machine on PHP 8.3 does not acquire the 8.5 image because it exists — the version is in the repository name, so "already here" answers it exactly. The old tool pulled a hardcoded list and cost everybody gigabytes for lines they never ran.
Within a line it does move forward: a tag like :3 is refreshed in place, and an exact 3.1.0 is taken to the newest 3.x. Crossing to 4.x is reported rather than done, because that is a decision about what a project runs, not an update. It only says "refreshed" when the digest actually moved.
dqx update is the project-scoped one: the images this project declares, whether or not they are here yet.
Asking a domain what it answers
bash
dqx verifyEvery declared domain, with the status it actually returns. A 200 is not proof the application works, but a 404 or a certificate warning tells you where in the chain to look — see domains, DNS and certificates.
Logs
bash
dqx logs # everything
dqx logs app # one serviceStarting over
bash
dqx down && dqx up # the ordinary reset
dqx clean # also removes the data directories a bind-mounted database leaves behind
dqx kill # every project on every backend, and free 80 and 443
dqx kill --all # that, and the runtimes toodqx clean is the one that removes data. It asks first.
When dqx itself is the problem
dqx doctor reports what it can see. If it is silent and something is still wrong, the honest next step is the logs and then the issue tracker — with dqx info --json and dqx doctor --json attached, which is everything somebody else needs to see what you see.