Run the Sites as rootless Podman Quadlets, and build with podman

The fleet on emily is rootless Podman with Quadlet units under
~/.config/containers/systemd and state in ~/podman-data, all behind one shared
Caddy unit. This brings the menus in line instead of leaving them the last
Docker deployment. The build moves with them: podman reads a Containerfile as
readily as a Dockerfile, so the name now says which toolchain is real.

The app is one .container and one .network file. Two details carry it.
UserNS=keep-id maps the image's own node account onto the user that owns the
units, so data/ and site.env stay owned by tanshu: plain tar backups, no
subuids. provision-site-env.sh writes site.env with the same mapping rather
than --user, which under rootless Podman would have produced a subuid-owned
file the app could not read. And the Site registers its own network with Caddy
by writing a drop-in beside caddy.container, so Caddy never lists its upstreams
and never changes when a Site is added. The unit keeps the Docker-era container
name for the scripts and muscle memory that look it up, while the Caddyfile
proxies to a dotless NetworkAlias, because a container name with dots in it is
a liability in DNS.

The unit keeps podman's Pull=missing, so a restart or a reboot never depends on
the registry being reachable, while the play pulls explicitly at deploy time.
That is also where a missing `podman login` for the private registry now fails,
with the instruction, rather than as a unit that will not start.

copy-site-data.sh moves a Site's data across, and is not a plain rsync:
/var/lib/menu.* on monoco belongs to uid 1000 while the ssh user there is 1001,
so the read needs sudo; monoco cannot ssh to emily, so the workstation relays
the tar; and the extraction runs as tanshu on emily, which is what makes the
files land owned by the user keep-id maps the app onto. It proves the copy by
comparing a SHA-256 of the whole tree against the source and checking every
file's owner.

Not yet done: the copies themselves and the DNS move, which need a human.
This commit is contained in:
tanshu committed 2026-10-16 09:54:52 +05:30
1 parent c412181159
commit e68f8ab00d
23 files changed
+876 -389

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
# Sites run as rootless Podman Quadlets behind a shared Caddy
Each Site is deployed as a **rootless Podman** container managed by a systemd
user unit (Quadlet) on `emily`, reached through the one shared Caddy unit rather
than a long-lived rootful Docker container. The image is built by `podman` too,
from a `Containerfile`. This matches how every other service on that host is
deployed, and it takes the deployment off Docker entirely.
The app owns its whole serving path, so it needed the smallest deployment of the
fleet: one `.container` file, one `.network` file, and a block appended to the
Caddyfile. Two details carry the design. `UserNS=keep-id:uid=1000,gid=1000` maps
the image's own `node` account onto the user that owns the units, which is what
keeps `data/` and `site.env` owned by a real host user — plain `tar` backups, no
subuids — and `scripts/provision-site-env.sh` uses the same mapping so both sides
agree on who owns the file. And the Site registers its own network with Caddy by
writing a drop-in beside `caddy.container`, so Caddy never lists its upstreams
and never has to be edited when a Site is added or removed.
**Considered Options**: staying on rootful Docker on `monoco`, rejected because
the fleet is moving and leaving one app behind means two runtimes and two Caddy
configs to reason about; running rootful Podman, rejected because it keeps the
same root-owned data and loses the plain-backup property; and naming the Quadlet
unit after the container (`menu-app.<domain>`), rejected because the dots make
the unit name awkward and the name fragile in DNS — the unit is `menu-<site>`
while the container keeps its old name for the scripts that look it up.
**Consequences**: the app is not published on a host port at all — only Caddy,
on the shared Podman network, can reach it — and deployment, logs and restarts
move from `docker` to `systemctl --user` / `podman`. The data root moves to
`~/podman-data/menu-<site>`, so each Site's content has to be copied across from
`/var/lib/menu.<domain>` once, before DNS points at it.