Audit of the per-app install hooks for singleton assumptions. The naming work
so far made identities unique, but a second copy still has to bind its own
ports, and `8201:80` is the same 8201 for every instance — the second container
simply fails at compose-up. `random:<internal>` is what makes an app
instanceable, since portAllocate then hands each instance its own host port.
Seven apps are caught: pihole (53 tcp+udp), stalwart (25/465/587/993), unbound
(5335 tcp+udp), traefik (443), searxng (8083), vaultwarden (8201), stoat
(7881). The message distinguishes the two cases, because they need opposite
fixes: an arbitrary pin like vaultwarden's 8201 should just become random,
while a DNS server on 53 or a mail server on 25 is genuinely one-per-host and
should never be instanced.
Runs before anything is cloned — this is a property of the app, not of the
instance. Bookstack is unaffected (all its ports are already random).
The rest of the hook audit found nothing further to fix:
- No hook writes to another app's config or deployed directory. The three that
reference ${containers_dir}traefik / headscale only test [[ -d ]] to detect
whether those are installed.
- Only two hooks read a foreign CFG_ namespace, and both are system-wide
settings (CFG_DOCKER_INSTALL_TYPE, CFG_ENABLE_VIDEO), not another app's.
- No app declares a fixed container IP; all come from IP_TAG allocation.
- Host-level writes are limited to wireguard's sysctl IPv4-forwarding drop-in
(global and idempotent) and its /etc/wireguard/params conflict probe. Traefik
writes only under $containers_dir$app_name. Stalwart's /etc/stalwart path is
inside its container.
Not mechanically checkable, so left as maintainer judgement: gluetun is a
network provider other apps join via network_mode container:gluetun-service,
and it plus wireguard hold NET_ADMIN and /dev/net/tun. Both are one-per-host
for reasons no guard can see.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>