Find the symptom below, then read the log it names.
If something is wrong#
Start with what is running:
docker compose ps -a
Healthy is db healthy, migrate exited with 0, and api up. migrate
showing as exited is correct: that is a finished migration, not a crash.
Then read the log of the service the symptom points at:
docker compose logs <service>
<service> is db, migrate, api or runner. The runner service exists
only under its profile, so it is listed only when you started it with one. Add
--since 10m to see only the last ten minutes, and -f to follow the log as
it is written.
Outside Development the API logs JSON, one object per line, and Compose sets
DOTNET_ENVIRONMENT=Docker on migrate and api. So their logs read as JSON
lines, not as the plain text of a dotnet run.
Docker is not running#
the system cannot find the file specified, naming a pipe or a socket. In full it isopen //./pipe/dockerDesktopLinuxEngineon Windows, or/var/run/docker.sockon macOS.Cannot connect to the Docker daemon, orerror during connect. The same cause, as another build of Docker words it.
Nothing about the stack failed: Docker Desktop is installed and not running. Start it and wait for its dashboard to say Engine running — a fresh install does not start itself, and the first start brings WSL2 up with it and can take a couple of minutes.
docker version
It printing a Server block as well as a Client one is how you know Docker is ready; then run the command that failed again. If the installer asked for a sign-out or a reboot and did not get one, that is the other half of this: the group membership it added is not in effect until then.
There is no service log to read here. The stack never started, so there is
nothing yet for docker compose logs to show.
The board does not come up#
migrateis exited. Correct, and not the symptom. It runs once, applies the migrations and stops.apiis inCreated.migratefailed, andapiis waiting for amigratethat never completed successfully. Read its log, fix what it names, and start the stack again:docker compose logs migrate docker compose up -dUpgrading says what a good
migratelog looks like.apiis restarting. It exits at start, for example because the schema is not there; it does not wait for one. Read why:docker compose logs apidbis not healthy. Nothing after it starts. Read:docker compose logs dbno matching manifest, orexec format error. The images do not suit this machine's processor.
The first start builds from source and takes several minutes, so a docker compose ps -a that shows nothing up yet is not by itself a failure.
docker compose up -d is safe to repeat.
The port is taken#
Two ports are published.
The board, 8080 by default. Move it with
HATCH_PORT, in a.envfile besidecompose.yaml:HATCH_PORT=9090or in front of the command:
HATCH_PORT=9090 docker compose up -dIn PowerShell:
$env:HATCH_PORT=9090; docker compose up -dThen point the runner at the new address:
hatch config --origin <your-hatch-origin>The database, 5432.
port is already allocated, orbindnaming5432, means another Postgres on this computer already holds it. Stop that one. Unlike the board's port, this one cannot be moved:compose.yamlhas no setting for it.
The log to read for either is the one that failed to start: docker compose logs api for the board, docker compose logs db for the database.
Signing in#
- The
migratelog says "Google sign-in is not configured: set Auth__Google__ClientId and Auth__Google__ClientSecret" (FirstAdminNotice.cs:16): nobody can sign in until Google is set up. SetHATCH_GOOGLE_CLIENT_IDandHATCH_GOOGLE_CLIENT_SECRET, then readdocker compose logs migrate. - Google shows
redirect_uri_mismatch:HATCH_GOOGLE_REDIRECT_URImust equal the address registered with Google, character for character. See Behind a reverse proxy. - Signed in, but nothing changes on a LAN address or in Safari: the sign-in
cookie is
Secure, and a browser holds it back on a plainhttpaddress. Same link: Behind a reverse proxy. - "An administrator has to approve your account" (
auth/src/App.tsx:62): the sign-in worked and the role ispending. An admin has to change it; see People, roles and keys. - Locked out: see Locked out.
- Any refusal:
docker compose logs apishows a WarningAuth refused {Reason} for {Host}{Path} from {ClientIp}(AuthGate.cs:304). The reasons areno_credential,unknown_grantandunknown_key(AuthGate.cs:28-32).
A 401 or a 403#
- A
401. The wall is on and no key was sent, or a key was sent and refused. Runhatch config. - A
403. A route or a rule behind it refuses — a key is never an administrator.hatchprints the API's own reason: a role-gate code, or a plain sentence naming the rule. Onlykey_not_acceptedandscope_mismatchprint as "the key is good and this route is not one it may take" - those two really are about a scope.
401 and 403 has the full list, from the other side.
The runner's own troubleshooting — no claude CLI on PATH, which branch is
the trunk, a tree that will not stash, one loop per checkout, a pass that
picks up nothing, losing the connection to origin — belongs to running the
loop, not to the board: see When it will not
run.
Two loops on one machine#
the workspace <dir> is already served. Another loop holds that workspace: When it will not run.this run will not clone. Not a fault, the default workspace is another loop's: When it will not run.
The Runner page offers no downloads#
- This build published no runner binaries. An image built before the
publish step, or
dotnet runfrom a checkout, has none. Build them yourself withmake publish-hatchfrom the repository root, or rebuild the image.
A psql shell#
For looking at the database by hand:
docker compose exec db psql -U user -d hatch