The first deploy of #117 failed its own health gate: audiobookshelf.service came up active (running), the engine logged a clean database init, and then — Listening on port :80 and Serving from base path "/audiobookshelf". Nothing on :8000, so wait_for_health refused at 180 s. The unit was healthy-looking and deaf at the same
time, which is the worst shape a gate can catch.
Cause
ABS_PORT, ABS_CONFIG_DIR, ABS_METADATA_DIR, ABS_PUID/ABS_PGID are the npm
package's environment names, printed by the server README. The container image never
reads them. Its own contract, taken off the pinned digest:
and the server source greps for exactly PORT, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH, HOST, JWT_SECRET_KEY… So the port fell back to the image
default 80, the router kept its /audiobookshelf prefix, and the two path variables
were honoured only because the image already exports the same values I was trying to
set — a coincidence that made the inert lines look correct.
ROUTER_BASE_PATH=/ also drops the prefix, so the service is the plain http://192.168.0.235:8000 the docs and the homepage card already promise. The dead ABS_* lines are deleted rather than kept as decoration.
Proven against the same digest before touching the unit — a throwaway container with
temp state dirs and a loopback-only port, removed afterwards:
[Server] Serving from base path "/"
Listening on port :8000
GET /healthcheck -> 200 OK
GET /audiobookshelf/healthcheck -> 404
Guards and docs
One assertion in tests/dankserver_quadlet.bats: the unit sets PORT=8000 and ROUTER_BASE_PATH=/, and carries noEnvironment=ABS_* line. That is the
regression, not a re-pinning of a constant.
The vault page gains the law — speak the image's env names, not the README's — with
the failure signature (active (running) + probes refused) and the two-second podman inspect --format '{{.Config.Env}}' check that settles any such question.
Its Shape table now names the env vars behind the port and the base path.
Deploy after merge
./scripts/deploy.sh dankserver --service audiobookshelf, then the homepage card on
Dankclaw. The database initialized correctly on the failed attempt (the image's own
path defaults matched the binds), so no state cleanup is involved — and no user data
exists yet: the owner account is still the first-run step, which only Frank can do,
from the LAN.
## The failure this fixes
The first deploy of #117 failed its own health gate: `audiobookshelf.service` came up
`active (running)`, the engine logged a clean database init, and then — `Listening on
port :80` and `Serving from base path "/audiobookshelf"`. Nothing on `:8000`, so
`wait_for_health` refused at 180 s. The unit was healthy-looking and deaf at the same
time, which is the worst shape a gate can catch.
## Cause
`ABS_PORT`, `ABS_CONFIG_DIR`, `ABS_METADATA_DIR`, `ABS_PUID`/`ABS_PGID` are the **npm
package's** environment names, printed by the server README. The container image never
reads them. Its own contract, taken off the pinned digest:
```
$ podman inspect --format '{{.Config.Env}}' <image>
PORT=80 CONFIG_PATH=/config METADATA_PATH=/metadata SOURCE=docker …
```
and the server source greps for exactly `PORT`, `CONFIG_PATH`, `METADATA_PATH`,
`ROUTER_BASE_PATH`, `HOST`, `JWT_SECRET_KEY`… So the port fell back to the image
default 80, the router kept its `/audiobookshelf` prefix, and the two path variables
were honoured only because the image already exports the same values I was trying to
set — a coincidence that made the inert lines look correct.
## The fix
Speak the image's names:
```
Environment=PORT=8000
Environment=ROUTER_BASE_PATH=/
Environment=CONFIG_PATH=/config
Environment=METADATA_PATH=/metadata
```
`ROUTER_BASE_PATH=/` also drops the prefix, so the service is the plain
`http://192.168.0.235:8000` the docs and the homepage card already promise. The dead
`ABS_*` lines are deleted rather than kept as decoration.
Proven against the same digest before touching the unit — a throwaway container with
temp state dirs and a loopback-only port, removed afterwards:
```
[Server] Serving from base path "/"
Listening on port :8000
GET /healthcheck -> 200 OK
GET /audiobookshelf/healthcheck -> 404
```
## Guards and docs
+ One assertion in `tests/dankserver_quadlet.bats`: the unit sets `PORT=8000` and
`ROUTER_BASE_PATH=/`, and carries **no** `Environment=ABS_*` line. That is the
regression, not a re-pinning of a constant.
+ The vault page gains the law — *speak the image's env names, not the README's* — with
the failure signature (`active (running)` + probes refused) and the two-second
`podman inspect --format '{{.Config.Env}}'` check that settles any such question.
Its Shape table now names the env vars behind the port and the base path.
## Deploy after merge
`./scripts/deploy.sh dankserver --service audiobookshelf`, then the homepage card on
Dankclaw. The database initialized correctly on the failed attempt (the image's own
path defaults matched the binds), so no state cleanup is involved — and no user data
exists yet: the owner account is still the first-run step, which only Frank can do,
from the LAN.
---
cwd: /home/blank/.omp/wt/wt-20261030-163847-6d1fc1f · session: 01a18f2e-18b1-7573-bcd5-ac99636f8bcb
The first deploy of the unit failed its own health gate in the worst possible shape:
systemd said active (running), the engine logged a clean database init, and the
server then listened on :80 under base path /audiobookshelf - so every probe of
:8000 was refused and wait_for_health timed out at 180s beside a container that
looked healthy.
Cause: ABS_PORT / ABS_CONFIG_DIR / ABS_METADATA_DIR / ABS_PUID are the npm
package's names, printed by the server README; this image never reads them. Its own
contract, read off the pinned digest with podman inspect, is PORT=80
CONFIG_PATH=/config METADATA_PATH=/metadata, and the server greps for exactly PORT,
CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH. The two path variables appeared to
work only because the image already exports the same values - the coincidence that
made the inert lines look correct.
The unit now speaks the image's names, adds ROUTER_BASE_PATH=/ so the service is
the plain host:port the docs and the homepage card promise, and the dead ABS_*
lines are deleted instead of kept as decoration. Proven against the same digest in
a throwaway container (temp state dirs, loopback-only mapping, removed after):
base path "/", listening on :8000, GET /healthcheck -> 200, and the old prefixed
path -> 404.
Guards: the quadlet seam now refuses an Environment=ABS_* line and requires
PORT/ROUTER_BASE_PATH; the vault page gains the law with its failure signature and
the two-second image-inspect check. Suite: ALL TESTS PASSED, 812 ok.
dank
merged commit 7a8c6bd2e3 into main2026-11-02 02:28:45 -08:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The failure this fixes
The first deploy of #117 failed its own health gate:
audiobookshelf.servicecame upactive (running), the engine logged a clean database init, and then —Listening on port :80andServing from base path "/audiobookshelf". Nothing on:8000, sowait_for_healthrefused at 180 s. The unit was healthy-looking and deaf at the sametime, which is the worst shape a gate can catch.
Cause
ABS_PORT,ABS_CONFIG_DIR,ABS_METADATA_DIR,ABS_PUID/ABS_PGIDare the npmpackage's environment names, printed by the server README. The container image never
reads them. Its own contract, taken off the pinned digest:
and the server source greps for exactly
PORT,CONFIG_PATH,METADATA_PATH,ROUTER_BASE_PATH,HOST,JWT_SECRET_KEY… So the port fell back to the imagedefault 80, the router kept its
/audiobookshelfprefix, and the two path variableswere honoured only because the image already exports the same values I was trying to
set — a coincidence that made the inert lines look correct.
The fix
Speak the image's names:
ROUTER_BASE_PATH=/also drops the prefix, so the service is the plainhttp://192.168.0.235:8000the docs and the homepage card already promise. The deadABS_*lines are deleted rather than kept as decoration.Proven against the same digest before touching the unit — a throwaway container with
temp state dirs and a loopback-only port, removed afterwards:
Guards and docs
tests/dankserver_quadlet.bats: the unit setsPORT=8000andROUTER_BASE_PATH=/, and carries noEnvironment=ABS_*line. That is theregression, not a re-pinning of a constant.
the failure signature (
active (running)+ probes refused) and the two-secondpodman inspect --format '{{.Config.Env}}'check that settles any such question.Its Shape table now names the env vars behind the port and the base path.
Deploy after merge
./scripts/deploy.sh dankserver --service audiobookshelf, then the homepage card onDankclaw. The database initialized correctly on the failed attempt (the image's own
path defaults matched the binds), so no state cleanup is involved — and no user data
exists yet: the owner account is still the first-run step, which only Frank can do,
from the LAN.
cwd: /home/blank/.omp/wt/wt-20261030-163847-6d1fc1f · session: 01a18f2e-18b1-7573-bcd5-ac99636f8bcb