-
Notifications
You must be signed in to change notification settings - Fork 0
Managed Hugo Upgrades
Since #980/#990 (v1.8.3), the server can optionally stage, activate, and roll
back official Hugo releases through four tools: get_hugo_update,
stage_hugo_upgrade, activate_hugo, rollback_hugo. This page is the
runbook for the actual arleo.eu deployment — for the full config reference
see docs/operator-guide.md § "Managed Hugo Upgrade Configuration" (that
table is the source of truth for field meanings; this page is not a copy of
it, see Documentation Source of Truth).
-
get_hugo_updateis read-only and safe to call anytime.check_latest:truemakes one bounded outbound request to the official Hugo GitHub API. -
stage_hugo_upgradedownloads, checksum-verifies, and extracts a release into a private managed directory. It never touches the livehugobinary. -
activate_hugoatomically swaps a managed symlink — never/usr/local/bin/hugoor a package-manager path — and never restarts the service itself. An explicit operator restart is required afterward for the swap to take effect. -
rollback_hugoatomically restores the previous managed target, same no-auto-restart rule.
All four require a write bearer. Staging/activation/rollback additionally
require hugo_upgrade.enabled: true in config — status reporting works even
when disabled.
build_site invokes the bare command hugo and lets the OS resolve it via
$PATH (internal/tools/admin/build.go). activate_hugo only ever changes
where hugo_upgrade.binary_link points — if that symlink's directory isn't
first on the service's PATH, an activation "succeeds" but the next
build_site silently keeps using whatever hugo resolved to before. This is
exactly the kind of drift class that caused the #981/#983/#984 incidents
earlier in the v1.8.x cycle — verify this before trusting an activation.
/etc/systemd/system/mcp-hugo-server-go.service.d/hugo-managed-path.conf
/etc/mcp-hugo-server-go/config.yaml (hugo_upgrade: block, appended)
The drop-in sets:
[Service]
Environment=PATH=/var/lib/mcp-hugo-server-go/hugo/current:/usr/local/bin:/usr/bin:/bin/var/lib/mcp-hugo-server-go was already in the base unit's ReadWritePaths
(used for the OAuth token store and other state), so managed_dir needed no
new systemd grant — only the PATH drop-in.
config.yaml has:
hugo_upgrade:
enabled: true
managed_dir: /var/lib/mcp-hugo-server-go/hugo
binary_link: /var/lib/mcp-hugo-server-go/hugo/current/hugo
require_extended: true
allow_downgrade: falseAfter editing either file:
ssh hugo-vm 'sudo systemctl daemon-reload && sudo systemctl restart mcp-hugo-server-go'Enabling this was config-only: prepending an empty managed directory to
PATH is safe, since a missing/empty PATH entry is simply skipped by lookup
and /usr/local/bin/hugo (the existing install) resolves exactly as before.
Confirmed in this order, live:
build_site -> status: ok (proves PATH change didn't break Hugo resolution)
get_hugo_update(check_latest=true) -> managed_upgrades_enabled: true, installed 0.147.0, latest 0.164.0 available
stage_hugo_upgrade(v0.164.0, dry_run=false) -> checksum_verified: true, version_verified: true, staged: true
activate_hugo was deliberately not called — enabling the capability is
not the same as performing an upgrade. The staged v0.164.0 sits at
/var/lib/mcp-hugo-server-go/hugo/versions/v0.164.0/, mode 0700, owned by
the mcp-hugo-server-go service account (confirmed via sudo stat; the
jm operator account correctly cannot read it directly — that's the design,
not a bug).
get_hugo_update(check_latest=true)
stage_hugo_upgrade(target_version="vX.Y.Z", dry_run=false)
activate_hugo(target_version="vX.Y.Z", dry_run=false)
ssh hugo-vm 'sudo systemctl restart mcp-hugo-server-go'
get_hugo_update # confirm installed.version now matches
build_site # confirm the new binary actually builds
If anything looks wrong after restart, rollback_hugo(dry_run=false) then
restart again — the previous binary's checksum is re-verified before the
symlink is restored, so a tampered or missing previous binary fails closed
rather than silently reactivating something unverified.
Do not enable hugo_upgrade.enabled: true without also adding the PATH
drop-in first — the tools will happily stage and "activate" a new Hugo
version whose binary build_site never actually picks up, and you'll spend
time debugging a phantom version mismatch instead of a one-line systemd
config gap.