Skip to content

Powered by Grav

Classic major OS upgrade

Classic major OS upgrade

The explicit per-hop path for major OS upgrades: set exactly one _${FROM}_TO_${TO}=YES control variable in /root/.barracuda.cnf, then run barracuda up-lts system. This is the manual alternative to the automated codename chain — the same per-codename machinery, but you name each hop and run each pass yourself.

Reach for the classic path when you need finer control than the automated drivers give: an intermediate Debian-to-Debian hop, stopping at a specific codename, or staying on Debian rather than migrating to Devuan.

Backup, test the backup, then proceed. This path involves more manual intervention than the automated one, so the snapshot discipline matters more, not less. Take a fresh VM snapshot, restore it to a throwaway VM, verify it boots and BOA works, then proceed. There is no in-place rollback.

Pre-flight

SH
# Refresh the BOA meta-installers
wget -qO- https://files.boa.io/BOA.sh.txt | bash

# Clean baseline pass
screen
barracuda up-lts system

# Reboot and confirm boot is clean before triggering the hop
boa reboot

If the reboot is not clean, fix it before setting any _${FROM}_TO_${TO} variable.

Devuan → Devuan hops

Edit /root/.barracuda.cnf and set exactly one of:

SH
_DAEDALUS_TO_EXCALIBUR=YES   # Daedalus → Excalibur
_CHIMAERA_TO_DAEDALUS=YES    # Chimaera → Daedalus
_BEOWULF_TO_CHIMAERA=YES     # Beowulf → Chimaera

Then run the upgrade pass:

SH
barracuda up-lts system

Each variable drives a single codename advance. To walk multiple codenames, complete one hop (including its reboot), confirm the host is stable, then set the next variable and run another pass.

Debian → Devuan hops (the migration path)

SH
_TRIXIE_TO_EXCALIBUR=YES     # Debian 13 → Devuan Excalibur
_BOOKWORM_TO_DAEDALUS=YES    # Debian 12 → Devuan Daedalus
_BULLSEYE_TO_CHIMAERA=YES    # Debian 11 → Devuan Chimaera
_BUSTER_TO_BEOWULF=YES       # Debian 10 → Devuan Beowulf
_STRETCH_TO_BEOWULF=YES      # Debian  9 → Devuan Beowulf
_JESSIE_TO_BEOWULF=YES       # Debian  8 → Devuan Beowulf

These pairings match the codename map in lib/functions/system.sh.inc and the auto* drivers: Buster/Stretch/Jessie all land on Beowulf as their Devuan entry point, then chain forward from there.

BOA is no longer regularly tested on Debian. Use only with a specific reason to stay on Debian, and accept the testing gap.

SH
_BOOKWORM_TO_TRIXIE=YES      # Debian 12 → Debian 13
_BULLSEYE_TO_BOOKWORM=YES    # Debian 11 → Debian 12
_BUSTER_TO_BULLSEYE=YES      # Debian 10 → Debian 11
_STRETCH_TO_BUSTER=YES       # Debian  9 → Debian 10
_JESSIE_TO_STRETCH=YES       # Debian  8 → Debian  9

Percona sequencing

  • Daedalus defaults to Percona 5.7. Stage forward in place with barracuda up-lts system percona-8.0 then barracuda up-lts system percona-8.4. No direct 5.7 → 8.4 jump.
  • Excalibur requires Percona 8.4. No 5.7 or 8.0 path on Excalibur — stage the DB to 8.4 on Daedalus first, then take the _DAEDALUS_TO_EXCALIBUR hop.

Before the first Percona step, run codebasecheck --box --deep: the DB server is shared, so the oldest codebase on the box gates the upgrade — see Percona 8 readiness.

The staged mechanics and recovery guards are in Percona install + tuning.

After the hop

One timing fact worth knowing on this classic path: the self-correcting PHP rebuild after a shared-library soname bump runs immediately after the dist-upgrade completes, before the first post-upgrade reboot (the automated drivers defer the same rebuild to their post-reboot step). Either way each version is offline only while its own rebuild runs (seconds on the prebuilt package path, roughly 3–5 minutes when it compiles from sources); it needs no operator action and is not a hang — see the automated chain's downtime note for the fuller description.

Unlike the automated chain, a classic barracuda up-<tier> system hop does not auto-deactivate unused PHP versions. The automatic cleanup (_php_if_versions_cleanup_cnf_AUTO_PHP_CLEANUP) fires only when a run-to-*.cnf marker is present or you have set the /root/.allow-php-multi-install-cleanup.cnf marker yourself — and only on an UPGRADE pass on a host that already carries an Octopus instance (/data/u) and a healthy Ægir Master. On the classic path you drive that pruning explicitly:

SH
barracuda php-idle disable   # deactivate every PHP version no site is using
barracuda php-idle enable    # re-install previously-disabled versions

php-idle disable is what writes the .allow-php-multi-install-cleanup.cnf marker; php-idle enable removes it and rebuilds the disabled versions.

ICU, resolvconf and the .top-* marker changes on the modern-codename path are the same as the automated path.

Debug markers for a hop

Two operator-created markers change what a pass does. Nothing in BOA writes either of them — you touch them yourself, and you remove them yourself.

/root/.debug.cnf has three unrelated effects, two of which belong to this path alone:

  • The init networking swap. Inside the major-OS-upgrade routine (_if_major_os_upgrade in lib/functions/system.sh.inc, which runs only when one of the _${FROM}_TO_${TO} variables above actually matches the box's current codename), right after the two-step apt phase, BOA moves /etc/init.d/networking aside to /etc/init.d/networking.bak, installs the distribution's shipped /etc/init.d/networking.dpkg-dist in its place with mode 755, and prints the result. The arm additionally requires the box to already carry the Ægir Master nginx includes (/var/aegir/config/includes/nginx_vhost_common.conf). Reach for it when a hop would otherwise leave the box running a BOA-era networking script the new release no longer agrees with. On an ordinary barracuda up-<tier> system pass this arm cannot fire at all.
  • The devpts/ptmx repair. Later in the same routine, and again only when the marker is present, BOA checks /proc/mounts for devpts and looks for /dev/pts/ptmx; if both are missing on a kernel that is not the legacy -beng one, it recreates and clears /dev/pts, installs udev, appends the devpts line to /etc/fstab and mounts it. Without the marker that check never runs.
  • The Nginx log-retention widening, suppressed. This effect is not hop-specific, and it is the only thing the marker pair still gates outside the hop. On a fresh install of a hosted box, the pass that lays down /var/xdrago also widens Nginx log retention in /etc/logrotate.d/nginx: every rotate 52, rotate 74 and rotate 14 becomes rotate 365, and the whole rewrite is skipped anyway when the file already carries rotate 9999. With .debug.cnf present and .default.cnf absent, that widening does not run and whatever retention the file already carries stands. On an upgrade pass the same widening runs unconditionally on hosted boxes and reads neither marker. The long-query TTL retune this guard used to gate has been retired as inert — the details are on Discontinued features.

The two hop effects apply on the automated chain too: the run-to-*.cnf drivers write the same per-hop variables, so both paths run through the same routine.

/root/.default.cnf does nothing on its own. Its only reader anywhere is the second leg of that co-guard, which makes the pair a tri-state:

Markers present Nginx log-retention widening
neither runs (the normal case)
.debug.cnf only skipped — the existing retention stands
.debug.cnf and .default.cnf runs again

That arm is hosted-only and fresh-install-only. On a self-hosted box, and on every upgrade pass anywhere, neither marker is consulted for it at all.

On hosted BOA boxes both files are erased for you. The fleet normalisation block in BOA.sh.txt (_update_agents, gated on _VMFAMILY=HOSTED plus the Ægir Master alias, /data/u and /var/xdrago) removes /root/.default.cnf and /root/.debug.cnf on every pass, and clear.sh triggers that pass every five minutes whenever no BOA run holds /run/boa_run.pid. Neither marker latches there: set them immediately before the run that needs them, and expect them gone afterwards.

.debug.cnf is not the _DEBUG_MODE variable in /root/.barracuda.cnf. That one only makes a run verbose; the two are read independently, and setting one does nothing for the other.

Package downgrades during a hop

A third marker in this family, /root/.allow.downgrades.on.same.os.dist.upgrade.cnf, is unlike the two above: BOA touches it itself during a hop. Before the pre-reboot leg it creates the marker so the post-reboot dist-upgrade leg can run apt-get dist-upgrade --allow-downgrades — a resumed hop may need to replace a package with an older build from the new release's repositories, which plain dist-upgrade refuses. The marker is consumed and deleted after it is honoured, so it is a one-shot each time.

Two things follow for an operator watching a hop:

  • Seeing INFO: dist-upgrade with --allow-downgrades option is used in the log (or the marker briefly existing) is normal hop behaviour, not a sign someone tampered with apt.
  • The lever is also yours: on a same-OS dist-upgrade pass that is stuck on a package apt refuses to downgrade, touch the marker and re-run — the pass runs its dist-upgrade with --allow-downgrades once and removes the marker itself.

Classic vs automated — which to use

  • The automated drivers cover four Devuan targets — Beowulf, Chimaera, Daedalus and Excalibur (one auto* driver each) — and walk the whole chain from one trigger. There is no automated driver for a Debian → Debian hop.
  • The classic path supports every codename in the supported set, including the Debian → Debian hops the automated drivers cannot do, but you drive each step.
  • From a typical start (Buster, Bullseye, Bookworm, Beowulf, Chimaera) heading to Daedalus, prefer the automated path. Use the classic path for exotic intermediates or fine control.

© 2026 BOA Documentation. All rights reserved.