Skip to content

Powered by Grav

xmass — whole-server move

xmass — whole-server move

xmass is the whole-server tool: it moves a complete BOA host — with every account, database and Solr index — to a freshly installed target in a single operation, keeping the databases current with live replication so the actual cutover window stays short. This page is the full runbook. For where xmass sits against the per-account xoct/xcopy tools, and for the machinery all three share — the storage-aware DRY/--live gate, the http-off write freeze, and the migration proxy the source becomes — see the Cross-host migration overview.

Running the migration

xmass moves an entire BOA host — every Octopus account, every Solr index, every config — from source to target. The DB transport is xtrabackup snapshot + MySQL GTID replication, not per-account mydumper cycles, so it scales to fleets of large databases where per-account dump/restore would be impractical.

A common reason to reach for xmass is an OS upgrade by fresh install rather than in place: instead of walking an ageing host through the in-place OS upgrade paths — a multi-reboot codename chain whose only rollback is a VM-snapshot restore — install BOA fresh at the same release on a new host running the newer OS, xmass the whole estate across, and cut over, leaving the old box as a migration proxy until DNS is repointed.

The working steps (init, sync, status, cutover) all run on the source and take the target IP as their argument; replication and rsync flow source → target. post-mig is run on the target after DNS is updated, and the initial pre-mig is run on both source and target — pre-mig parks the BOA background runners (runner.sh, owl.sh, usage.sh, graceful.sh, manage_ltd_users.sh) so the task queue is frozen during the move, and exchanges root SSH keys between the hosts.

SH
# Phase 0 — pre-mig, run on BOTH source then target
xmass pre-mig <source-fqdn>

# On SOURCE — install xtrabackup, enable GTID, snapshot,
# restore on target, start the replica
xmass init <target-ip> [--permanent-proxy]

# On SOURCE — rsync platforms/files/configs/Solr (repeat freely);
# DRY plan by default, re-run with --live to transfer
xmass sync <target-ip>                                 # DRY plan
xmass sync <target-ip> --live

# On SOURCE — replication lag + last-sync timestamp
xmass status <target-ip>

# On SOURCE — final cutover (block web, drain lag, promote target,
# renameaegirhost on target, convert source to proxy); the DRY run
# plans storage for all accounts + shared and stops before any
# destructive step
xmass cutover <target-ip> [--permanent-proxy]          # DRY plan
xmass cutover <target-ip> [--permanent-proxy] --live

# On TARGET — finalise after DNS update
xmass post-mig

Dispatch verbs: pre-mig, init, sync, status, cutover, post-mig. The sync and cutover verbs are gated by the DRY/--live model (see Storage-aware transfers and the DRY/--live gate on the overview); one DRY plans all accounts plus shared.

Pre-sync is incremental — once init runs, repeat sync over days or weeks until ready; the GTID replica keeps the target's databases current and sync carries the file-system deltas.

Each sync run carries a fixed set of filesystem data to the target: shared BOA data (/data/all, /data/disk/all, /data/disk/arch, /data/disk/legacy), the static web root (/var/www/static), DNS zones (/etc/bind), usage logs (/var/log/boa/usage), the Solr indices, and per-account platforms, site files, drush site-aliases, nginx vhosts, SSL/LE material, and the FTP account's SSH keys (/home/oN.ftp/.ssh). MySQL data is the one thing not rsynced — GTID replication keeps it current continuously.

Whole-server cutover downtime is typically 1–3 hours.

You do not pre-create matching Octopus accounts on the target. xmass restores the full xtrabackup snapshot, brings databases across via GTID replication, and enumerates the accounts dynamically from /data/disk/ — no Octopus instances are required on the target beforehand.

xmass init auto-installs the matching percona-xtrabackup-* package and auto-enables GTID on both servers if not already active.

init also chooses its xtrabackup transfer method automatically from free space on the source /. With more than 1.5× the data-directory size free it stages — the backup is written to /var/backups/xmass_stage, prepared, then rsynced to the target. With less it streams — piped via xbstream over SSH straight to /var/backups/xmass_restore on the target and prepared there, needing no staging disk on the source.

xmass cutover invokes xoct proxy per account during the cutover phase to convert the source vhosts to proxies, plus renameaegirhost on the target for the master and each Octopus account.

--permanent-proxy

--permanent-proxy marks the source as a permanent HTTP proxy after cutover rather than a temporary one pending DNS update. It is not cosmetic:

  • It keeps the migration-proxy realip/CSF trust on the target in place — the post-mig teardown becomes a deliberate no-op (migration_proxy_trust.sh teardown is skipped).
  • It drops a /data/conf/xmass_permanent_proxy.pid flag so notification wording reflects the permanent mode.
  • It prints the permanent mode in the cutover summary.

The flag can be set at init (_PERM_PROXY_FLAG) and overridden at cutover--permanent-proxy on the cutover command wins over whatever was set at init.

Requirements

  • Identical Percona versions on source and target — GTID replication is version-strict. (This is the key difference from xoct/xcopy, which are cross-version safe via mydumper.)
  • BOA installed on the target at the same release as the source.
  • Root SSH key access source → target, set up by xmass pre-mig.
  • The source IP whitelisted in CSF on the target before init, so xtrabackup streaming and rsync can reach it.

The cutover sequence

Before any destructive step, cutover runs an automatic pre-flight: it confirms the phase is syncing, that none of the BOA background runners parked by pre-mig are still active, and that system cron is stopped — refusing to proceed otherwise.

The cutover itself has built-in safety timings. It drains replica lag to zero, polling every 15 s for up to 30 minutes and aborting on timeout; takes a final static/files-only rsync under a FLUSH TABLES WITH READ LOCK to catch last-second uploads; then triple-checks lag = 0 at 10 s intervals, unlocking the source and aborting if any check fails.

Once replication is decoupled and nginx is serving proxied traffic on the target, cutover rewires panel database access for every Ægir root before the renames run. The init-time datadir swap replaced the target's MySQL wholesale, so the fresh-install panel databases the new host's control-panel dirs pointed at no longer exist; for each root xmass rediscovers the live (replicated) hostmaster database, resets that database user's password across its host variants, and rewrites the panel dir's stored credentials to match. This must succeed before renameaegirhost can bootstrap, and it is idempotent — a root whose panel already names a live database is skipped. After promotion cutover starts cron and restores the runner scripts on the target, and writes a proxied.pid marker for every source account.

MySQL credentials and the datadir swap

Because the xtrabackup restore replaces the target's entire /var/lib/mysql — including the mysql system tables — the target's MySQL root password becomes the source's. xmass therefore ships /root/.my.pass.txt and /root/.my.cnf from source to target twice: right after the restore, so client tools work during replica setup, and again after promotion at cutover. This wholesale swap is also why the panel-database rewire above is required.

State machine and recovery

xmass tracks its progress in /data/conf/xmass_state.cnf (mode 600 — it holds the replication password), moving through init → syncing → cutover → complete. Each subcommand checks the current phase and refuses to run out of sequence. To abandon a migration and start over, remove the state file — but only after replication has been torn down on the target, or the next init collides with a live replica.

Recovery paths:

  • init fails before replication starts — remove the state file and retry.
  • init fails after replication starts — on the target run STOP SLAVE; RESET SLAVE ALL, drop the xmass_repl user on the source, then remove the state file.
  • cutover aborts mid-flight — source MySQL is unlocked automatically, but the source stays on its 503 freeze; remove static/control/http-off.pid from each account and reload nginx to restore service before investigating.
  • A panel rewire or a renameaegirhost pass fails — cutover parks resumably at phase=rename-failed and names the affected roots. Fix the cause and re-run xmass cutover <target-ip> --live to resume; already-rewired and already-renamed roots no-op.

© 2026 BOA Documentation. All rights reserved.