xoct & xcopy — single-account move and copy
xoct moves one Octopus account between BOA hosts and decommissions the source to a
migration proxy; xcopy shares its dispatcher shape but is a non-destructive copy —
the source stays live. This page is the full runbook for both. For where these sit
against the whole-server xmass tool, and for the machinery all three share — the
storage-aware DRY/--live gate, the http-off write freeze, and the migration proxy —
see the Cross-host migration overview.
xoct — single-account move
xoct is a multi-step dispatcher, not a two-verb prep/import model. Almost every
step runs on the source with the target IP as the argument — including create,
which SSHes to the target to provision the new Octopus account there (boa in-octopus).
Only import and post-mig are run on the target; pre-mig is run on both. The
optional fourth argument renames the account on the target.
pre-mig (run on the source first, then the target) freezes the task queue for the move:
it parks the BOA background runners — runner.sh, owl.sh, usage.sh, graceful.sh,
manage_ltd_users.sh — as .off and kills any running copy, then exchanges root SSH keys
so root can SSH freely between the two hosts. post-mig restores those runners afterwards.
# Phase 0 — pre-mig, run on BOTH source then target
xoct pre-mig <source-fqdn>
# On SOURCE — provision target shared infra + first-pass transfer.
# transfer/pretransfer default to a read-only DRY run: review the
# [DRY-PLAN] output, then re-run the same command with --live.
xoct transfer shared <target-ip> # DRY plan
xoct transfer shared <target-ip> --live
xoct create o1 <target-ip> [o2]
xoct pretransfer o1 <target-ip> [o2] # DRY plan
xoct pretransfer o1 <target-ip> [o2] --live
# On SOURCE — export (writes http-off 503 + dumps) then transfer
xoct export o1 <target-ip>
xoct transfer o1 <target-ip> [o2] # DRY plan
xoct transfer o1 <target-ip> [o2] --live
# On TARGET — import, then post-mig (restores runner scripts on target)
xoct import o1 <target-ip> [o2]
xoct post-mig <source-fqdn>
# On SOURCE — convert old host to proxy, then post-mig
xoct proxy o1 <target-ip> [o2]
xoct post-mig <source-fqdn>
# Then update DNS A records to <target-ip>; the proxy hop drops away
# once DNS propagates.
The dispatch verbs are: export, create, import, pretransfer, transfer,
proxy, pre-mig, post-mig, and ssl-gen (re-issue proxy SSL, gated on
/root/.ssl.proxy.cnf).
transfer shared provisions the account-independent shared infrastructure on the
target — the shared codebases (/data/all, /data/disk/all), archives and SQL dumps
(/data/disk/arch), the Solr cores (/opt/solr4, /var/solr7/data, /var/solr9/data),
/var/www/static, /etc/bind, and the usage logs under /var/log/boa/usage. The
per-account verbs take the Octopus username (o1, o2, …) as their second argument and
the target IP as their third.
When the optional fourth argument differs from the source username, xoct enters
rename mode on import. renameaegirhost rewrites every reference from the source to
the target account name via its --old-account/--new-account axis: the control-panel
identity becomes o2.<target-fqdn> (adopting the fresh install's panel site dir and its
valid credentials), and /data/disk/o1 paths in platform, git, backup and package records
are rewritten to /data/disk/o2 — otherwise the first DB-driven regeneration would
re-stamp the stale source paths over the transfer-time fixes. Customer-site URIs are
never touched: a site on the account subdomain such as shop.o1.<fqdn> keeps its name.
The rewrite also covers the per-site FPM $user_socket account token, so the renamed site
is served by its own account's pool rather than the target's install-time account that
still carries the old name.
The target account o2 must not already exist before create — create provisions
it fresh. After a rename, verify the site returns a real 200 direct to the target
(not a proxy or catch-all): a site-wide 403 immediately after a rename historically meant
a socket token still pointed at the wrong account's pool.
The transfer/pretransfer verbs are gated by the DRY/--live model — see
Storage-aware transfers and the DRY/--live gate
on the overview.
The export dump
export dumps the entire hostmaster database (${_HDB}, derived from the
hostmaster site's drushrc.php) into /data/disk/<oN>/src/prev_hostmaster.sql,
using the same mysqldump option set renameaegirhost uses (--single-transaction --quick --no-autocommit --skip-add-locks --no-tablespaces --hex-blob).
The previous hand-curated table allowlist — tied to the removed direct-SQL import
surgery below — silently dropped any hostmaster table not on the list, so newly added
module/field schema tables simply vanished from the migrated instance; the full dump
gives the import target a faithful copy for renameaegirhost's rewrite and 5-pass
queue.
The same applies to xcopy export; xmass is unaffected (full-server replication, no
hostmaster mysqldump).
Source disk space: export writes the per-site mydumper output and the hostmaster
dump under /data/disk/<oct>/src/, so keep at least 1× the account's total database
size free there before you start.
Chained migrations — a former target moving onward: a box that was itself a migration
target earlier still holds src/prev_hostmaster.sql and log/imported.pid from that
transfer. xoct export will not write a fresh dump while prev_hostmaster.sql is
present (it protects a dump a pending import may still need), so a chained move would ship
the stale dump; imported.pid also blocks the account from being an import target again.
Before migrating a former target onward, remove both — rm -f /data/disk/o1/src/prev_hostmaster.sql /data/disk/o1/log/imported.pid. (log/sourcefqdn.txt
is rewritten with every dump and needs no cleanup.)
On-target import
Before importing, on the target: pause the BOA task runner so no background job fires
mid-import — run service cron stop, then chmod 644 /data/all/cpuinfo, and wait about
five minutes for any in-progress tasks to settle. Run the import under websh: point
/bin/sh and /usr/bin/sh at it first — ln -sfn $(which websh) /bin/sh; ln -sfn $(which websh) /usr/bin/sh — and confirm with ls -la /bin/sh.
On the target, import reduces to:
- re-import of the full hostmaster DB dump,
- a couple of frontend
variable-setcalls via drush (site_frontpage, plushosting_client_send_welcomein the fix path — Ægir API, not direct SQL), then renameaegirhost --aegir-root.
renameaegirhost handles the in-place hostname rename of the moved account — a full
5-pass Ægir task queue that rewrites Drush aliases, nginx vhost files under
config/server_master/, and the Ægir DB content as its atomic rename step.
This supersedes the legacy "migrated/merged instance" fixup layers, which were removed
from import:
- the direct-SQL surgery (in-place
sedof hostmaster drush aliases plus hardcoded-niddrush sqlqREPLACE/UPDATE/DELETE againsthosting_context/hosting_package/node/hosting_site/hosting_platform/users_roles, guarded by a one-shotpost-merge-fix.pid), and - the ghost/empty-platform delete loop, whose missing-
sites/alltest was true for every valid Composer D8+ platform and so deleted valid platforms from Ægir during import.
xcopy import follows the same model; the intended remaining xoct/xcopy divergence
is that xcopy does not enable the migration proxy.
Operational notes
- Re-running after a failure — most
xoctsteps are pid-gated and safe to repeat after a failed run. To force a step to run again, remove its marker under/data/disk/o1/log/(for exampleexported.pid,transferred.pid,imported.pidorproxied.pid). - Drupal 6 IP blocking — Drupal 6 sites that block by IP can lock out the migration.
Before migrating such a site, whitelist the source IP at
/admin/user/rules(a Host rule of type Allow), or flush the site's{access}table via Chive afterwards.
No automatic fixups on upgrade or nightly runs
Migrated/merged instances get no automatic surgery elsewhere either. The legacy fixups that used to rewrite imported instances on upgrade and nightly runs were all removed:
- the hardcoded-node-ID SQL rewrites against hostmaster's
hosting_context/node/hosting_sitetables, - the greedy
sed"Pre-Fix" rewrites ofhostmaster.alias.drushrc.php, - the nightly one-shot
UPDATE hosting_context SET name=hostmasterSQL, and - the nightly
_fix_site_readonlymodereset.
They predate renameaegirhost and, on long-lived instances whose node IDs no longer
matched the hardcoded assumptions, caused hostmaster drift rather than fixing it.
After a cross-host migration, hostname/topology fixups are exclusively the job of
renameaegirhost and the normal hosting machinery — do not expect BOA to auto-rewrite
hostmaster contexts or aliases on the next upgrade or nightly run.
The retired internal markers (post-merge-fix.pid, hmpathfix.pid,
hosting_context.pid, readonlymode_fix.info) need no operator action.
DNS proxy conversion
DNS-proxy setup is not automatic: it is the explicit xoct proxy step run on the
source, which converts the account's nginx vhosts to proxy templates forwarding to the
target IP, removes the migration http-off, and emails the account owner. After DNS is
repointed the proxy hop is no longer used.
Downtime and cross-version safety
Downtime per account: minutes to a few hours depending on site sizes. Sequential — one
account at a time. xoct uses mydumper/myloader for the DB phase and is
cross-version safe — it works between hosts running different Percona versions.
xboa is gone
xboa was the predecessor name. It is fully removed — there is no xboa binary and
no xboa fetch entry (BOA fetches xoct, xcopy, and xmass; nothing under the
old name).
xoct is its functional successor with two improvements:
- the Ægir DB hostname replacement and post-import task queue are delegated to
renameaegirhost(more thorough, 5-pass), and - the hardcoded internal-account email exclusion was removed so
xoctworks correctly when driven byxmass.
If muscle-memory still types xboa, an operator-created compatibility symlink is the
documented workaround (BOA does not ship one):
ln -sfn /opt/local/bin/xoct /opt/local/bin/xboa
The symlink target must be the real binary at /opt/local/bin/xoct (xoct is fetched
to /opt/local/bin only — it is not mirrored into /usr/local/bin); both directories
are on PATH, so a bare xboa then resolves.
xcopy — duplicate an account, source stays live
xcopy shares xoct's dispatcher shape but is a non-destructive copy: it does
not write http-off on the source, and it does not convert the source to a
proxy. Use it to stand up a duplicate of an account on a second host (staging clone of a
whole instance, pre-cutover rehearsal) while the original keeps serving.
xcopy pre-mig <source-fqdn> # on SOURCE + TARGET
xcopy transfer shared <target-ip> # on SOURCE — DRY plan
xcopy transfer shared <target-ip> --live # on SOURCE
xcopy create o1 <target-ip> [o2] # on SOURCE
xcopy pretransfer o1 <target-ip> [o2] # on SOURCE — DRY plan
xcopy pretransfer o1 <target-ip> [o2] --live # on SOURCE
xcopy export o1 <target-ip> # on SOURCE (dumps, no http-off)
xcopy transfer o1 <target-ip> [o2] # on SOURCE — DRY plan
xcopy transfer o1 <target-ip> [o2] --live # on SOURCE
xcopy import o1 <target-ip> [o2] # on TARGET
xcopy noproxy o1 <target-ip> [o2] # on SOURCE (skip proxy conversion)
xcopy post-mig <source-fqdn> # on SOURCE + TARGET
xcopy ssl-gen # on TARGET (re-issue certs)
Dispatch verbs: pre-mig, export, create, import, pretransfer, transfer,
noproxy, post-mig, ssl-gen.
The noproxy verb is the deliberate counterpart to xoct proxy — it finalises the copy
without proxying the source.
ssl-gen re-issues Encryption on the target for the (now duplicate) account, which is
required because the copied account, like any name-changed account, lands with
Encryption disabled.
Because the source is never frozen, an xcopy produces a point-in-time duplicate that
diverges from the still-live source the moment it completes — it is a copy, not a
synchronised replica.
Related
- Cross-host migration overview — which tool when, and the
shared storage gate,
http-offfreeze and migration-proxy machinery these tools stand on. - xmass — whole-server move — the whole-host tool that drives
xoct proxyper account during cutover. - Site cloning & in-host migration — the within-host Clone and Migrate tasks for a single site.
- Aliases & redirects — the alias/redirect model, relevant when a moved or renamed account changes its canonical hostname.
- Database (MySQL/Percona) —
mydumper/myloaderinternals behind the DB phase. - See the Reference appendix for the consolidated variable, command, and control-file tables.