Suite Docker Delivery¶
Status: design / direction. Captured 2026-06-16. The Docker form of the eMuleBB Suite bundle. Companion to SUITE-INSTALLER (the Windows-local form); same TrackMuleBB brain, two targets.
Model¶
A committed docker-compose.yml driven by compose profiles + a .env, with a
minimal setup.sh; TrackMuleBB's setup CLI can generate/update the compose
(setup --target docker) so there is one source of truth, no wiring drift.
TrackMuleBB runs as a plain service (no Docker socket). Docker is rust-only
for the eD2K core (MFC is Windows-only).
Decisions (2026-06-16)¶
| # | Decision |
|---|---|
| 1 | Our GHCR images: rust + qBittorrentBB-nox are linuxserver-style (s6, PUID/PGID, TZ, /config+/data); TrackMuleBB + Bountarr are minimal. |
| 2 | Single /data volume (torrents/ usenet/ ed2k/ media/) shared by all → hardlink + atomic-move. Model 1: eD2K through Arr (rust = qBit-emulated client). |
| 3 | Gluetun (optional): only the P2P containers route through it; ports published on gluetun; control plane reached at gluetun:<port>. |
| 4 | Compose lives in trackmulebb/docker/; TrackMuleBB is a plain service (no docker.sock). "Install a component" = enable a profile + docker compose up. |
| 5 | Bind-mounts under the project dir (./config/<app>, ./data) for self-containment; Plex in bridge + ports (claim token manual). |
Images (GHCR) — the enabling prerequisite¶
Four custom images we publish to ghcr.io/emulebb/* (latest + versioned), built/
pushed by per-repo CI. Without these the Docker bundle cannot start.
| Image | Repo | Convention |
|---|---|---|
emulebb-rust |
emulebb-rust | linuxserver-style (s6, PUID/PGID, /config, writes downloads to /data) |
qbittorrentbb-nox |
qbittorrentbb | headless nox Linux build of the fork + linuxserver-style |
trackmulebb |
trackmulebb | minimal Python (uv); control plane on X_LOCAL_IP equivalent |
bountarr |
itlezy/bountarr | minimal Node (v22) |
Third-party images are upstream: Prowlarr/Sonarr/Radarr/SABnzbd (linuxserver.io),
Plex (official), Gluetun (qmcgaw/gluetun).
Storage & permissions¶
- One
/datamount for every download client + Arr + Plex →torrents/ usenet/ ed2k/ media/. Same path everywhere enables hardlinks + atomic move; Arr promotes all three networks tomedia/. PUID/PGID/TZuniform across containers (set in.env).- Config/data are bind-mounts under the project dir (
./config/<app>,./data) — self-contained, inspectable, backup = copy the folder.
Networking & Gluetun¶
- Gluetun is optional (not everyone runs a VPN). When on, only rust +
qBittorrentBB use
network_mode: "service:gluetun"(fail-closed killswitch — the Docker analog of the Windows hide.me split-tunnel). Control plane, Arr/Prowlarr, SAB, Bountarr, Plex stay on the normal network. - Port wiring (gluetun gotcha): P2P containers have no own ports — publish
all their ports (qbbb WebUI + BT listen; rust
/api/v1+ eD2K TCP + Kad UDP) on the gluetun service, and wire Arr/TrackMuleBB togluetun:<port>. - Inbound (HighID): use the VPN provider's port-forwarding (gluetun supports it for built-in providers) and auto-set the client's listen port; otherwise LowID with a warning. UPnP does not work behind a VPN (the gateway is the tunnel) — do not rely on it.
- Provider: any. hide.me is not a built-in gluetun provider → use gluetun's
custom OpenVPN mode (supply hide.me's
.ovpn); this means no auto port-forwarding for hide.me, and the custom config must be validated. For best inbound on Docker prefer a PF-capable built-in provider. - Gluetun down = the P2P clients are fully isolated (incl. their API/WebUI) — safe by design; TrackMuleBB shows them offline.
- Gluetun = no is allowed freely (P2P on the normal network); the operator is then responsible for VPN/network safety elsewhere.
Compose & profiles¶
- Profiles per component (
COMPOSE_PROFILES=rust,qbbb,trackmulebb,prowlarr, sonarr,radarr,sabnzbd,bountarr,plex,gluetun) realise the selectable bundle without generating files. TrackMuleBB is always present; >=1 P2P client; dependency enforcement (Bountarr → Radarr/Sonarr). setup.sh(minimal): prompts →.env(components, Gluetun yes/no + provider,PUID/PGID/TZ, paths) →docker compose up.
# (illustrative skeleton, not the final file)
services:
gluetun: # profile: gluetun
image: qmcgaw/gluetun
cap_add: [NET_ADMIN]
ports: # P2P ports are published HERE
- 8080:8080 # qbbb WebUI
- 4711:4711 # rust /api/v1
# + BT/eD2K/Kad listen ports
qbittorrentbb: # profile: qbbb
image: ghcr.io/emulebb/qbittorrentbb-nox
network_mode: "service:gluetun"
volumes: [./config/qbbb:/config, ./data:/data]
environment: [PUID, PGID, TZ]
emulebb-rust: # profile: rust
image: ghcr.io/emulebb/emulebb-rust
network_mode: "service:gluetun"
volumes: [./config/rust:/config, ./data:/data]
trackmulebb: # profile: trackmulebb (plain service, no socket)
image: ghcr.io/emulebb/trackmulebb
# reaches rust/qbbb at gluetun:<port>
Relation to the Windows-local form¶
Same TrackMuleBB brain and the same selectable bundle; the difference is the network-safety mechanism (Windows = hide.me split-tunnel in-app; Docker = Gluetun namespace) and the runtime substrate (native processes vs containers).
Tracked work¶
- GHCR images:
emulebb-rust,qbittorrentbb-nox,trackmulebb,bountarr(per-repo CI). The enabling prerequisite. - TrackMuleBB Docker delivery: compose + profiles + Gluetun wiring +
compose-generation (
TMBB-FEAT-013). - The setup CLI's
--target dockermode (TMBB-FEAT-010).