Pre-flight, ordered cutover, post-restore verification, rollback, and the list of what Cloudron's backup does not cover. Written from a live inventory of the server on 2026-10-29; open unknowns are marked rather than guessed.
7.8 KiB
Cloudron server migration — Hostinger → Infomaniak
Pre-flight and cutover runbook for moving the co-op's Cloudron server. Written 2026-10-29 from a live inventory of the server. Owner: Nathan. Executor: whoever is at the console that weekend — assume it is not the person who wrote this.
The server being migrated hosts my.inference.coop and 16 apps. The agent host is a
separate machine (already on Infomaniak) and is NOT part of this migration.
1. What Cloudron's backup covers — verified, not assumed
- All 16 apps and their
/app/data(localstorage), plus addons: postgres, mysql, mongodb, redis. - Cloudron platform config: domains, dashboard users, app env vars, TLS config, backup target settings, scheduled tasks.
- The container registry app, 3.8 GB of custom images (
librechat,litellm,admin-panel,member-portal,member-dashboard). These exist nowhere else — if the registry does not restore, the custom apps cannot start. - Size check that confirms app data is included: ~5.8 GB of app data visible inside containers vs ~4.8 GB per backup run.
Apps: chat, chat-legacy (OpenWebUI), inference.coop (Surfer), forum (Loomio), matrix (Synapse), gateway (LiteLLM), panel, registry, git (Gitea), vault (Vaultwarden), portal, search (SearXNG), dashboard, newsletter (Listmonk), email (Cloudron Mail), cloud (Nextcloud).
2. What it does NOT cover
- The host OS layer. Ubuntu itself, netplan, cloud-init,
sshdconfig,authorized_keys, hostname, PTR, provider firewall, hand-edited/etc. Build the new box; do not expect to restore it. - The Cloudron licence/subscription — must be moved to the new server.
- DNS records. Manual at yoursrs.com (domains.coop NS). Cloudron cannot manage them.
- The backup target config and passphrase — chicken-and-egg by design.
- App data written to a host path instead of
/app/data— see the audit in §3. - Anything on the old box outside Cloudron (e.g.
/etctweaks). Diff before decommissioning.
Not a gap, despite looking like one: our custom images (registry is an app, backed up), and the agent's own state (different machine).
3. Pre-flight
Blocker — do this first. The Vault app (vault.inference.coop) runs on the server
being migrated, and the backup passphrase is believed to live there. The credentials
needed to restore are inside the thing being restored.
-
Export the Vault contents and store them off-server (personal password manager or offline media). Confirm the Cloudron backup passphrase is in your own hands.
-
Confirm the backup target's S3 access key is available off-box. The agent host has
~/.hermes/security/backup_sites_backup.json(0600) — check whether it carries the key; if not, retrieve it from the Infomaniak console. -
New host capacity:
df -h,free -m,lsb_release -a,cloudron --version. Must hold ~6 GB of app data plus Docker images and growth; check swap exists. -
OS: same or newer Ubuntu than the old box. Cloudron install version must be ≥ the old box's (9.0.4).
-
Confirm the Cloudron subscription can be moved to a new server.
-
Ask Infomaniak to set rDNS/PTR for the new IP to
my.inference.coop— mail deliverability depends on it. -
Open on the new box: 22, 80, 443, 465, 587, 993. Match the old box's exposure (
ufwis inactive there; Cloudron manages its own iptables — do not add ufw rules that fight Docker). -
Mount audit on the Cloudron host — the one class of data the backup cannot see:
sudo docker ps -q | xargs -I{} sudo docker inspect {} \ --format '{{.Name}} {{range .Mounts}}{{.Source}}=>{{.Destination}} {{end}}' \ | tr ' ' '\n' | grep -v /app/data A container showed `/var/lib/nextcloud` mounted from the host. Identify its owner and size; if it holds real data, copy it separately. -
Record the highest backup run number before the freeze.
-
Lower all A-record TTLs to 300 s a day early (currently 760–1642 s ≈ 13–27 min).
-
List external allowlists that carry our IP: Regolo, GreenPT, Tinfoil, any mail relay, and inbound webhooks. Update after cutover.
-
Announce the window in
#updates.
4. Cutover, in order
- Freeze writes. Pause the portal's
membership_sweepandrag_refresh, hold Listmonk campaigns. Chat cannot be frozen — note the exact snapshot time. - Final backup on the old box. Run "Backup now" and confirm state
normal. Everything after this timestamp is lost in a forward migration. - Stop the old box's backup schedule (Settings → Backups → disable). Two Cloudrons
writing one target interleave and produce
EntityTooLargequota failures. - Build the new box and restore from the Infomaniak S3 target, using the passphrase from §3. Expect TLS to fail until DNS moves — reach the dashboard by IP or a temporary override.
- Switch the A records to the new IP.
- Wait out the TTL, then confirm certificates issued.
- Re-enable backups on the new box — once daily, retention 7 days. (The approved change was never applied; the API has no route for it, so do it in the dashboard.)
- Update external allowlists. Leave the old box running read-only.
5. Post-restore verification
- Dashboard loads with a valid certificate; all 16 apps
running. - One custom app loads — e.g.
panel.inference.coop. This is the proof the registry restored; without it nothing custom starts. - Data spot-checks: Matrix login + a room loads; Gitea repo list; portal
/admin/overviewreturns the expected member count; gateway/v1/models. - Mail: send to and receive from an external address; MX resolves to the new IP; SPF
still
a:my.inference.coop; DKIM keys survived (they are in app data); PTR correct. - Federation:
curl -s https://matrix.inference.coop/_matrix/federation/v1/version. Other homeservers cache the old IP and will retry it for a while — that is expected. - Agent API access:
cloudron listfrom the agent host. The API token lives in Cloudron's config so it should survive; if it does not, mint a new one and update the agent's.env. - Schedules fire once, not twice: portal sweeps, Listmonk, agent digests.
- First backup on the new box is
normaland in the expected size range.
6. Rollback
- Keep the old box powered on, read-only, for several days.
- Rollback = point DNS back. Do not delete old snapshots from S3 until the new box has at least one verified backup run.
- Know the asymmetry: anything written between the final backup and the DNS switch exists only on the old box. Rolling forward discards it; rolling back keeps it. Keep that window short and quiet.
7. Open unknowns — resolve before cutover
- Which app owns the
/var/lib/nextcloudhost mount, and how much data is in it. - The Cloudron host's OS version, disk and RAM (not visible over the API).
- Whether the S3 access key is in the off-box JSON or only inside Cloudron.
- Whether any provider API key is bound to our current IP.
- Whether the subscription permits a server move.
8. Pitfalls this server has already produced
- Quota failures: 4.82 GB per run twice daily hit the Infomaniak ceiling
(
Upload exceeds quota). Retention is 7 days; schedule must be once daily. - The once-a-day change cannot be made over the API — dashboard only. Historically it was reported as done and was not.
cloudron update --imagereads the manifest from the current directory. Running it from the wrong repo fails withmanifest id does not match, which looks like a server problem and is not.- The agent host is not migrated but will see errors while DNS moves. It needs no
config change: every reference is a hostname, including
--server my.inference.coop.