Skip to content

fix(calibre-web): apply the reader model on upgrades, not just fresh installs - #158

Merged
pofallon merged 1 commit into
mainfrom
fix/calibre-web-reader-model-on-upgrade
Sep 19, 2026
Merged

pofallon merged 1 commit into
mainfrom
fix/calibre-web-reader-model-on-upgrade

Conversation

@pofallon

Copy link
Copy Markdown
Contributor

What

Bundle 1.2.1 → 1.2.2. All app.db work moves into one stamped script the sidecar runs on every deploy, plus a label that forces the app container to be recreated so the change is actually read.

Why — #157 shipped a fix that could not run

I wrote 1.2.1's Guest grant deliberately outside the first-run guard so existing installs would get it. It never had a chance: everything this package writes to app.db lives in a /custom-cont-init.d hook, and that only executes when the app container starts. A release that changes only the init sidecar leaves the calibre-web service's compose config byte-identical, so docker compose up -d correctly leaves that container alone — and the hook never runs.

Observed on a live host (Hola 0.11.0-rc.4), upgrading 1.1.2 → 1.2.1:

container StartedAt   2026-09-07      <- never recreated by the upgrade
sidecar   StartedAt   2026-09-19      <- ran, rewrote the hook file
app.db                config_anonbrowse = 0,  Guest 32
/opds with the correct Traefik credential -> 401

Anonymous browsing is the half that was never migrated at all — it was only ever set on first run. 1.1.x left /opds and /kobo/ exempt with no gate and Calibre-Web's own Basic auth in front of them; 1.2.x moves that gate to Traefik, which strips the credential once verified (try-hola/hola#455). An upgraded install therefore gets the gate without the anonymous browsing that makes it serviceable, and answers 401 to every reader that just passed it. That is strictly worse than 1.1.x, where readers at least worked.

How

  • One script, two triggers. The sidecar writes /config/.hola-apply-reader-model and runs it immediately when app.db already exists (the upgrade). When it does not exist yet — a fresh install, where s6 creates app.db later — the custom-init hook runs the same script. No duplicated logic.
  • Stamped one-shot. /config/.hola-reader-model-v1 written on success; from then on it is a no-op, so anything you change afterwards in the UI stands.
  • The library path keeps its own first-run-only guard — a configured install is never repointed.
  • io.try-hola.calibre-web.reader-model: "v1" on the calibre-web service. Changing a label changes the service config, so the upgrade recreates the container and Calibre-Web re-reads its settings. Without it the sidecar's writes sit unread — and hola restart will not help, because that is up -d too. Bump the label whenever a package change has to reach a running container. Not in sh.hola.*, which the platform owns and overwrites.

Verification

In the real image (lscr.io/linuxserver/calibre-web:0.6.26-ls393), four paths:

upgrade   (app.db exists, 1.1.x state)   anonbrowse 0 -> 1,  Guest 32 -> 290
re-run    (stamped)                      no-op
operator turns anon browsing back off    left alone (stays 0)
fresh     sidecar: writes nothing        hook applies once -> ('/books', 1, 1), Guest 290
          app.db exists but no tables    exits 0, no traceback (applies next boot)

README

Adds an Upgrading from 1.1.x section, including the part that bites operators: readers authenticated with a Calibre-Web account under 1.1.x and must be reconfigured to https://hola:<OPDS_BYPASS_PASSWORD>@<host>/opds from 1.2.x on.

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh

…installs

1.2.1's Guest grant was written to run on existing installs — and could not.
Everything this package writes to app.db lived in a custom-init hook, which
only executes when the APP container starts; a release that changes only the
init sidecar leaves the app container's compose config identical, so Hola's
upgrade (`docker compose up -d`) correctly does not recreate it and the hook
never runs. Observed on a live 0.11.0-rc.4 host: upgrading 1.1.2 -> 1.2.1 left
`config_anonbrowse = 0` and `Guest 32`, and /opds answered 401 to a caller
holding the correct Traefik credential.

Anonymous browsing is the half that was never migrated at all — it was only
ever set on first run. 1.1.x left /opds and /kobo/ exempt with no gate and
Calibre-Web's own Basic auth in front; 1.2.x moves that gate to Traefik, which
strips the credential once verified. An upgraded install therefore has the gate
without the anonymous browsing that makes it serviceable, and answers 401 to
every reader that just passed it.

Three changes:

- All app.db work moves into ONE script the sidecar writes, run from two
  places: the sidecar itself when app.db already exists (the upgrade), and the
  custom-init hook when it does not yet (the fresh install). A stamp file
  makes it a one-shot, so operator edits made later stand.
- The library path keeps its own first-run-only guard: a configured install is
  never repointed.
- A label on the calibre-web service, bumped when a change must reach a running
  container, so `up -d` recreates it and Calibre-Web re-reads its settings.
  Without this the sidecar's writes sit unread until something else restarts
  the app — and `hola restart` will not, since it is `up -d` too.

Exercised in the real image across four paths: upgrade (32 -> 290, anonbrowse
0 -> 1), re-run (stamped no-op), operator turning anonymous browsing back off
(left alone), and fresh install (sidecar writes nothing, hook applies once) —
including an app.db that exists before Calibre-Web has created its tables,
which now exits cleanly instead of raising.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
@pofallon
pofallon merged commit d8ce8e5 into main Sep 19, 2026
5 checks passed
@pofallon
pofallon deleted the fix/calibre-web-reader-model-on-upgrade branch September 19, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant