Self-hosted manga + books library with the *arr automation stack, running in Docker. Phase 1 targets a MacBook Pro; the same compose file is multi-arch and lifts onto a Raspberry Pi 5 (arm64) for Phase 2.
┌─────────────┐ indexers ┌──────────────┐
│ Prowlarr │──────────────▶│ FlareSolverr │ (solves Cloudflare)
│ (the brain) │ └──────────────┘
└──────┬──────┘
syncs apps │ feeds search results
┌──────────┴───────────┐
▼ ▼
┌─────────────┐ ┌───────────────┐
│ Kapowarr │ │ LazyLibrarian │ grabs sent to ↓
│ (comics) │ │ (books) │
└──────┬──────┘ └───────┬───────┘
└───────────┬───────────┘
▼
┌───────────────┐ downloads to /data/torrents
│ qBittorrent │───────────────┐
└───────────────┘ │ hardlink/import
▼
/data/media/{manga,books}
│ scanned by
▼
┌─────────────────┐
│ Kavita │ read on iPad / web
└─────────────────┘
mloader (official Manga Plus) ──▶ data/media/manga (supplementary source)
Everything shares a single ./data root so imports are instant hardlinks
(same filesystem) instead of slow copies.
| Service | URL | Role |
|---|---|---|
| Kavita | http://localhost:5000 | Reader / front-end (iPad, web) |
| Prowlarr | http://localhost:9696 | Indexer/tracker manager (the brain) |
| qBittorrent | http://localhost:8080 | Download client (torrents) |
| Kapowarr | http://localhost:5656 | Manga/comics manager |
| LazyLibrarian | http://localhost:5299 | Books manager (Readarr replacement) |
| FlareSolverr | http://localhost:8191 | Cloudflare solver (API, no UI) |
Run make urls to print these any time.
- Docker Desktop (Compose v2)
- Python 3 + pip for the optional official mloader path (
brew install python)
cp .env.example .env # adjust PUID/PGID/TZ if needed (defaults are fine on Mac)
make up # creates config/ + data/ trees, then docker compose up -d
make psPort 5000 busy? macOS Control Center / AirPlay Receiver often binds 5000.
- Preferred: System Settings → General → AirDrop & Handoff → turn off AirPlay Receiver.
- Or set
KAVITA_PORT=5500in.env(the container still listens on 5000; only the host port changes) and read Kavita at http://localhost:5500. make verifyauto-falls back to 5500 when 5000 is held.
Do these in order — each app needs the previous one configured.
- qBittorrent (:8080): grab the temporary admin password from the logs
(
make logs S=qbittorrent), log in, set a permanent password, and set the default save path to/data/torrents. - FlareSolverr (:8191): no UI — just confirm it returns JSON in a browser.
- Prowlarr (:9696):
- Add your indexers (Settings → Indexers).
- Add FlareSolverr (Settings → Indexers → add a FlareSolverr proxy, tag it).
- Add qBittorrent as a download client (Settings → Download Clients, host
qbittorrent, port8080). - Add Kapowarr and LazyLibrarian under Settings → Apps so Prowlarr auto-syncs indexers to them.
- Kapowarr (:5656): set root folder
/data/media/manga, download folder/data/torrents/comics, and connect qBittorrent (hostqbittorrent, port8080). - LazyLibrarian (:5299): set the eBook destination
/data/media/books, download dir/data/torrents/books, and connect qBittorrent + indexers. - Kavita (:5000): create your admin account on first run, then add two
libraries pointing at
/mangaand/books. These map todata/media/*, so anything Kapowarr/LazyLibrarian/mloader imports shows up after a scan.
Containers reach each other by name on the compose network — use
qbittorrent,flaresolverr,kapowarr, etc. as hostnames (notlocalhost) when wiring apps.
make download URL=https://mangaplus.shueisha.co.jp/titles/100017 LAST=1
# CBZ lands directly in data/media/manga → scan the Kavita manga libraryDrop EPUB/PDF into data/media/books/ and rescan the Kavita books library.
make up Start the full stack (creates dirs + .env first)
make down Stop and remove containers
make ps Container status
make logs [S=name] Tail logs (all, or one service)
make urls Print every service web UI
make download URL=<mplus-url> [LAST=1] Official Manga Plus download
make scan Prove media mounts inside the containers
make clean Remove app configs (keeps data/media) for a fresh wiring
make verify Full end-to-end Phase-1 verification
.
├── docker-compose.yml # 6 services (multi-arch: Mac now, Pi later)
├── .env.example # PUID / PGID / TZ — copy to .env
├── Makefile
├── scripts/
│ ├── download-chapter.sh # mloader → data/media/manga
│ ├── scan-library.sh # prove mounts
│ ├── wait-for-http.sh # generic UI readiness poll
│ ├── wait-for-kavita.sh # Kavita-specific poll
│ ├── free-port-5000.sh # free macOS AirPlay port
│ └── verify-phase1.sh # full-stack verifier
├── config/ # per-app config + DBs (gitignored)
│ ├── prowlarr/ qbittorrent/ kapowarr/ lazylibrarian/ flaresolverr/
├── data/ # single shared root (gitignored)
│ ├── torrents/{comics,books}/ # qBittorrent save dir
│ └── media/{manga,books}/ # imported library → Kavita /manga, /books
└── kavita-config/ # Kavita app config & DB
make verify
# or manually:
docker compose config
docker compose up -d
docker compose ps
for p in 5000 9696 8080 5656 5299 8191; do curl -sI localhost:$p | head -1; done
# prove the shared /data mount is identical across containers:
docker compose exec qbittorrent sh -c 'echo hi > /data/torrents/comics/_probe'
docker compose exec kapowarr sh -c 'cat /data/torrents/comics/_probe' # -> hi
docker compose down- Images: linuxserver.io (Prowlarr/qBittorrent/LazyLibrarian),
mrcas/kapowarr,ghcr.io/flaresolverr/flaresolverr,ghcr.io/kareadita/kavita— all arm64-ready. - Readarr is retired; LazyLibrarian is the active book-automation replacement.
- Use indexers only for content you're entitled to.
These are opt-in and don't affect the base make up / make verify:
| Command | What it does |
|---|---|
make vpn-up |
Run qBittorrent behind a gluetun VPN killswitch (set VPN_* in .env; then point download clients at host gluetun). See docker-compose.vpn.yml. |
make remote-up |
Join Tailscale + serve Kavita over HTTPS (set TS_AUTHKEY). |
make maintenance-up |
Run Diun → notify when container images have updates. |
make backup |
Tarball config/ (the brains) to ./backups, excluding media. |
make bootstrap-prowlarr |
Inventory Prowlarr + ensure the qBittorrent client exists. |
The stack is lift-and-shift portable (container-internal paths + hostnames),
so the whole config/ moves to the Pi with near-zero re-wiring, and all images
are arm64-ready. Full step-by-step — copy, Tailscale HTTPS, and the best iOS
reading apps — is in MIGRATION.md.