Both templates were stale in different ways, and the new tag enumeration
surfaced it: nextcloud sat on 31-fpm-alpine with 34 out, mastodon on
v4.2.0 with v4.6 out.
nextcloud 31-fpm-alpine -> 34-fpm-alpine
mastodon v4.2.0 -> v4.6
The mastodon one was the real problem: v4.2.0 is an EXACT patch pin, so
it never moved at all — no security patches, ever. v4.6 is a moving
minor-line tag (the same shape as stalwart's v0.16), so auto-update now
delivers patches within the line.
Deliberately NOT floated to :latest or :stable. Both projects require
stepped upgrades — Nextcloud in particular refuses to skip a major — so
a tag that crosses majors on its own would break the app on a routine
container recreate. A major-pinned, patch-moving tag is the correct
shape here, not a limitation.
Verified against the registry: all three pinned apps now report nothing
newer, and each tag still moves (v0.16 2026-08-10, 34-fpm-alpine
2026-08-03, v4.6 2026-08-06).
Templates only, so this changes NEW installs. Existing installs keep
their tag and will now be told a newer line exists.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add a `#LIBREPORTAL|<APP>_VERSION_TAG|<current-tag>` sentinel to each app's
anchor (`<slug>-service`) image line, so a `CFG_<APP>_VERSION` config drives
that tag through the existing tagging system — the config becomes the source,
the compose tag is derived (like ports/IPs/domains already are). This is the
"install/pin a specific version" knob.
- Placeholder is the literal current tag (e.g. `31-fpm-alpine`), NOT a `*_DATA`
token: an unset var leaves the line untouched (inert comment, keeps the real
tag) and never trips the up_app stale-tag scanner.
- Only the anchor line is tagged (sidecars mariadb/redis/nginx stay
tracked-by-digest, not user-version-picked). ollama correctly targets
`ollama/ollama`, not the companion open-webui.
- Untagged anchors normalized to `:latest` (semantic no-op) so they're
templatable too. The manager (libreportal) is skipped — it updates via its
release channel, not a docker tag.
32 apps wired. Verified: CFG_NEXTCLOUD_VERSION=32 → `nextcloud:32` (prefix
preserved, idempotent, sidecars untouched); all compose files still valid YAML.
Inert until a version config is set, so no behaviour change on existing installs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Now that uid auto-discover is in (d424473), adding more apps is just naming the
container + path — no uid guessing. Four more apps get complete backups of their
private data dirs (previously: incomplete on the file side because libreportal
can't read sub-UID-owned content from the host).
- linkding-service -> /etc/linkding/data (overlaps with sqlite db, same pattern as gitea — dump replays over the captured tree on restore, harmless)
- vaultwarden-service -> /data (same overlap pattern)
- headscale-service -> /var/lib/headscale (same overlap pattern)
- mastodon-service -> /mastodon/public/system (uploads; postgres handled separately by backup.db)
Coverage now: nextcloud, bookstack, gitea, owncloud, linkding, vaultwarden,
headscale, mastodon. Skipped jellyfin — it has multiple internal sqlite DBs and
no backup.db declared; adding just backup.files without backup.db / backup.live
wouldn't activate live capture, and adding backup.live blind could yield torn
sqlites. That one wants proper DB declaration first.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
- Add libreportal.backup.db labels to the MariaDB/Postgres apps (nextcloud,
owncloud, bookstack, mastodon, invidious) so they back up live + consistent.
- If a declared dump cannot be taken (DB down, wrong path), the backup falls
back to stop-snapshot-start for that run instead of snapshotting torn data —
a misconfiguration degrades to 'safe with downtime', never to 'unsafe'.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
Replace the static one-host-per-app model with per-port routers: each
Traefik-managed port carries a subdomain (12-col PORT format) and gets a
DOMAINSUBNAME_TAG_<n> host, so one container can serve unlimited hosts.
tagsProcessorPortSubdomains stamps per-port hosts (subdomain @/empty = apex,
multi-level allowed); tagsProcessorPortRouterBlocks comments out
# TRAEFIK_PORT_<n>_BEGIN/END blocks for non-Traefik ports so unfilled
placeholders never ship (mirrors GLUETUN_OFF). Convert all 27 router apps
(subdomains seeded from HOST_NAME; headscale admin. prefix -> subdomain).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys,
Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun
VPN routing, and a web dashboard to manage it all.
Free & open forever to self-host; optional paid hosted services fund it.
See PROMISE.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>