Part 8: The First Real Workload — itg-prd-book
Everything before this was plumbing. itg-prd-book is the first host that actually does something: Calibre, calibre-web, and transmission, all as Quadlets, on a bootc image built by the pipeline.
Apps as Quadlets
A Quadlet is a .container (or .network) file that systemd turns into a generated service. You drop them in /usr/share/containers/systemd/ via the image and systemd runs them. Key rules I learned:
- Give each one
[Install] WantedBy=multi-user.targetso it auto-starts. Without it, it just sits there. - Do NOT
systemctl enablea Quadlet service. The generator handles it; enabling it by hand breaks things. AutoUpdate=registrypluspodman-auto-update.timeris what refreshes the app images (the 4 AM step from Part 7).
The apps are linuxserver.io images (lscr.io/linuxserver/...). Two settings make or break them: PUID/PGID must match the file owner (my worker user, 1001), and you set TZ. Get the PUID wrong and the app cannot write its own config, which looks like a hundred other problems.
Data disk vs NFS: pick per data type
This was a real design decision, not a coin flip:
- App databases go on a local disk. SQLite over NFS is a corruption risk because of file locking, so the app configs and the Calibre
metadata.dblive on a localdataBookXFS disk (Part 2's first-boot format pattern). - Bulk media and downloads go on NFS. They are big, sequential, and lock-free, so the NFS shares are perfect for them.
SELinux volume labels
Podman volume suffixes matter here:
:Zrelabels a volume private to one container (each app's/config).:zrelabels a volume as shared between containers (the Calibre library, which both calibre and calibre-web mount).- For the NFS download share you cannot relabel, so you pass
context=on the mount instead (Part 2).
The Proxmox VM
Build the disk image with bootc-image-builder (bib), which now requires --rootfs xfs explicitly and defaults to --local. Output lands as a qcow2. Then on Proxmox:
qm importdiskthe qcow2,qm setit asscsi0(OS) and add a second disk asscsi1(data).- VirtIO SCSI controller, not LSI (UEFI/OVMF has no LSI driver), q35 machine type, UEFI, and install qemu-guest-agent (already in the base).
The build has to finish and push before you create the VM, because you need the image to make the OS disk, then you add the data disk.
Lessons
- Quadlets need
WantedBy=multi-user.targetand must never besystemctl enabled. - linuxserver PUID/PGID has to equal the file owner or the app is read-only to itself.
- Databases on local disk (SQLite hates NFS locking), bulk media on NFS.
:Zprivate,:zshared,context=for NFS.- VirtIO SCSI on UEFI, every time.
Runbooks
The build
- Image Mode & Base
- Storage
- Hostnames & DNS
- Registry
- Actions Runner
- The Pipeline
- Nightly Auto-Deploy
- First Workload
- VSCode Cockpit
- Hugo Auto-Deploy
- Cloudflare & Kobo
- Runner Provisioning
- Tailscale Router
- SWAG & Website
- Split-DNS
- Plex
- Media Library Support
- The Day After
- Self-Hosted Media
- Hypervisor Joins the Fleet
- The Matrix Homeserver
Reference