Objective

Add manga (and manhwa/manhua) management to the media-stack — acquisition, serving, and metadata — following the stack’s zero-manual, declarative architecture.

Context

The stack handled TV, movies, and subtitles but nothing for manga. Manga is a separate pipeline from the *arr suite: no Prowlarr / qBittorrent / torrent indexers. Acquisition is extension-based scraping (Mihon/Tachiyomi sources), so it slots in beside the *arr apps rather than into them. Research surveyed the books/comics/manga landscape; manga was done first because it reuses the stack’s existing Flaresolverr for Cloudflare-protected sources.

Approach

Three new services on media-stack-net:

  • Suwayomi (ghcr.io/suwayomi/suwayomi-server:stable) — downloader; downloads chapters as CBZ + ComicInfo.xml into ${MEDIA_DIR}/manga. Direct port 4567 (subfolder support unreliable), like Seerr.
  • Komga (gotson/komga) — reader/server under the nginx /komga subpath via SERVER_SERVLET_CONTEXT_PATH. Handles RTL (manga) and continuous-vertical (manhwa/webtoon).
  • KOMF (sndxr/komf) — metadata from AniList/MangaUpdates; internal-only.

scripts/setup-komga.sh bootstraps the Komga admin headlessly via the claim API and declaratively manages the Manga library. scripts/setup-suwayomi.sh registers the extension repo(s) via the GraphQL API. Both are orchestrator-run, self-poll readiness, and are non-fatal so manga never blocks the *arr setup.

Outcome

Shipped and deployed to the live box. Verified fully end-to-end with real content: registered the keiyoushi repo, installed the Comick source, downloaded One Piece ch.1 as a CBZ (52 pages + ComicInfo.xml), and Komga indexed the “One Piece” series. Komga admin auto-claimed (amanschhina@gmail.com); creds in .env.

PRs (all merged): #36 (feature, 5126644), #37 (Suwayomi healthcheck wget→curl), #38 (setup-suwayomi.sh — extension-repo registration), #39 (declarative source installs + KOMF series covers, 892f1b2).

Follow-up (PR #39): made source selection and covers declarative. SUWAYOMI_EXTENSIONS (pkgName list) installs sources via the GraphQL updateExtension mutation, idempotently (default: Comick, MangaDex, Weeb Central). KOMF now uploads real provider posters — the One Piece series shows the MangaUpdates volume cover. Chapter-1 thumbnail stays black: the Comick/ArcaneScans release ships a 556-byte blank page 1 that Komga thumbnails (real content is page 2+); a source artifact, not fixable in config.

Next Actions

  • Feature + orchestration + docs + CHANGELOG merged (PR #36)
  • Deployed to the live box (.env creds set, mstack, docker compose up -d)
  • Smoke-tested the full path: One Piece ch.1 via Comick → CBZ → Komga indexed
  • Merge PR #38 (setup-suwayomi.sh)
  • Declarative source installs + KOMF series covers (PR #39, merged + redeployed)

Resources

Notes

Durable findings worth keeping:

  • Suwayomi vs Komga roles. Suwayomi can also read manga (it streams chapters live from sources and reads downloads), so for a solo browser-only reader it can stand alone. Komga adds value as the library server for owned/downloaded content: multi-user accounts with per-user progress, OPDS for mobile/e-reader apps (Mihon, Paperback, KOReader, Kindle), KOMF metadata/covers, collections/readlists, and offline permanence (sources can delist titles — see MangaDex below). Suwayomi = downloader + source browser; Komga = “Plex for your owned manga.”
  • Komga claim API (GET/POST /api/v1/claim with X-Komga-Email/X-Komga-Password) creates the initial admin non-interactively. Komga has no way to disable auth (gotson/komga#306 wontfix) and no reverse-proxy header trust (confirmed in SecurityConfiguration.kt — no RequestHeaderAuthenticationFilter), so the *arr X-Auth-User trick can’t make it skip login. Mitigation: “Remember me” → 365-day token (rememberMeDuration default, KomgaSettingsProvider.kt).
  • KOMF requires a committed config/komf/application.yml (crash-loops without one). Komga connection is env-only (KOMF_KOMGA_BASE_URI shadows the yml baseUri).
  • KOMF cover config must nest under komga:. seriesCovers/overrideExistingCovers live at komga.metadataUpdate.default; a top-level metadataUpdate block is silently ignored (metadata still updates, no poster uploaded). Re-cover an existing series via POST /komga/match/library/{libraryId}/series/{seriesId} to KOMF (port 8085). KOMF REST paths are under /komga, not /api/metadata.
  • A scanlator’s blank lead page breaks Komga’s chapter thumbnail. Comick/ArcaneScans One Piece ch.1 ships 001.webp as a 556-byte blank; Komga thumbnails page 1 → black cover. Source artifact, not config; bookCovers won’t fix it (no per-chapter provider covers).
  • Suwayomi extension repos are NOT settable via env. EXTENSION_REPOS/server.extensionRepos is ignored at runtime in v2.x (repos are DB-managed); the URL must be the /tree/<branch> form, not raw index.min.json. Register via the GraphQL setSettings API — what setup-suwayomi.sh does (PR #38).
  • MangaDex delisted One Piece (and many licensed titles) — 0 chapters. Comick carries the full series; used it for the smoke test.
  • jq '.isClaimed // empty' is a trap// treats boolean false as absent, and a missing key makes jq -r .isClaimed print the string "null" (exit 0). Branch on the boolean explicitly.
  • :latest + Watchtower is the stack’s real convention; a reviewer’s “everything is pinned” claim was false — verify against the compose, don’t trust it.
  • AI PR reviews regenerate on every push and ship occasional false “deploy-breaking” claims (e.g. “gotson/komga has no curl” — it does, at /usr/bin/curl). Verify empirically before acting.

Bulk-downloading a chapter range (Suwayomi GraphQL)

Backfilled One Piece’s Egghead arc onward (ch. 1058–1186, 130 chapters) via the API rather than the UI:

  1. fetchChapters(input:{mangaId}) to refresh the source chapter list (Comick returns ~2500 entries — multiple scanlators per chapter number).
  2. Dedupe by integer chapter number, picking one scanlator per number (prefer ArcaneScans/Bato over aggregators like Mangakakalot; aggregators are all that’s available for the most recent chapters). Skipping this downloads 2-3× duplicates.
  3. enqueueChapterDownloads(input:{ids:[Int!]!}) with the selected chapter ids.
  4. startDownloader(input:{}) — the downloader sits STOPPED after enqueue; it must be started explicitly. (input:{} is required even though empty.)
  5. Poll downloadStatus{ state queue{...} } until the queue drains.
  6. Komga does not auto-index reliably on filesystem events here — trigger POST /api/v1/libraries/{id}/scan (202), then poll series/{id}.booksCount.

With AUTO_DOWNLOAD_CHAPTERS on and the title in Suwayomi’s library, future chapters download automatically; only backfills need this manual flow.

Decisions

2026-06-27 — Manga is a separate pipeline, not wired into the *arr stack

Status: decided. Decision: Suwayomi (extension scraping) for acquisition, not Prowlarr/qBittorrent. Rationale: manga sources are scanlation sites, not torrent indexers; the *arr model doesn’t fit. Komga/KOMF serve and enrich.

2026-06-27 — Komga on nginx subpath, Suwayomi on direct port

Status: decided. Decision: Komga at /komga (Spring context-path is reliable); Suwayomi on direct port 4567. Rationale: Suwayomi’s reverse-proxy subfolder support is unreliable — same call as Seerr.

2026-06-27 — Keep :latest, reverted image pinning

Status: decided (reverted a mid-PR change). Decision: komga/komf use :latest. Rationale: matches the *arr services and the stack’s Watchtower auto-update model; pinning would opt them out of it.

Media Stack IaC Declarative Config Evaluation · Persist qBittorrent UPnP Port Mapping in Media Stack · Homelab Services Architecture