auberge dns migrate
Update all existing Cloudflare A records to a new IP. Alias: auberge d m.
auberge dns migrate [OPTIONS]Options
Section titled “Options”| Option | Description | Default |
|---|---|---|
-H, --host HOST |
Target host | Interactive |
-i, --ip IP |
Override IP (conflicts with --host) |
From host |
-n, --dry-run |
Preview without updating | false |
-o, --output FORMAT |
human or json |
human |
Target address
Section titled “Target address”The address goes through the same resolver set-all uses, so the two commands cannot read the same input as different addresses:
| Given | Resolves to |
|---|---|
-H <host> |
That host’s address from the inventory. An unknown name errors and names the rest |
-i <ip> |
The address as typed — the escape hatch for one the inventory does not hold |
| Neither | A picker listing each host with its address |
A migration target is nearly always a host auberge already knows, so -H is the usual flag and -i the exception.
Resolution happens before Cloudflare is reached, so an unknown host or an undrawable picker costs no API call and rewrites nothing.
Examples
Section titled “Examples”auberge dns migrate --dry-run # pick the host, always preview firstauberge dns migrate --host new-vpsauberge dns migrate --ip 10.0.0.5 # address outside the inventoryGotchas
Section titled “Gotchas”- Updates only existing A records. Doesn’t create new ones.
- Skips records whose current IP is in CGNAT range
100.64.0.0/10— a tailnet address is not a Host address, so repointing one would take a tailnet-only subdomain off the tailnet (ADR-0003). Each skip is reported: named underSkipping (holds a tailnet address, not a Host address)onhuman, and as a row in theskippedarray onjson. - Only A records under the domain are candidates. The apex, records in other domains, and every non-A type (CNAME, AAAA, TXT, MX, NS, SRV) appear in neither array.
wwwis not special-cased — it is untouched only when it is a CNAME, as it usually is. - An app in another DNS zone has its records there, not here, so
migratenever sees them.dnsresolves one zone per run (ADR-0081), so those apps are named in theoff_zonearray and on stderr — a report short of the roster would otherwise read as a completed migration. Migrating them is a second run, scoped to their zone.
VPS migration workflow
Section titled “VPS migration workflow”auberge ansible bootstrap new-vps --ip 10.0.0.5auberge ansible run --host new-vpsauberge backup restore latest --from-host old-vps --host new-vpsauberge dns migrate --host new-vps --dry-runauberge dns migrate --host new-vpsdig +short cal.example.com # verifyJSON output
Section titled “JSON output”migrated and skipped partition the records the run took as candidates, so a caller diffing against the zone can tell a record left behind on purpose from one the run never saw. migrated carries every in-scope record with the outcome of its write, failures included — read success, not membership. off_zone is neither: it names apps whose records are in a zone this run does not hold, so they were never candidates and no old_ip exists to report.
{ "migrated": [ { "subdomain": "rss", "old_ip": "203.0.113.10", "new_ip": "10.0.0.5", "success": true } ], "skipped": [ { "subdomain": "bichon", "ip": "100.64.0.9", "reason": "tailnet_only" } ], "off_zone": [{ "app": "forgejo", "subdomain": "git", "zone": "shop" }]}| Array | Field | Type | Description |
|---|---|---|---|
migrated |
subdomain |
string | Subdomain label |
migrated |
old_ip |
string | IP before migration |
migrated |
new_ip |
string | IP after migration (the resolved target) |
migrated |
success |
boolean | Cloudflare update succeeded |
skipped |
subdomain |
string | Subdomain label of the untouched record |
skipped |
ip |
string | CGNAT address the record keeps |
skipped |
reason |
string | Always tailnet_only (ADR-0003) |
off_zone |
app |
string | App whose Zone this run does not hold |
off_zone |
subdomain |
string | Its effective subdomain |
off_zone |
zone |
string | The Zone its record lives in |
migrated and skipped rows appear in the order Cloudflare listed the zone; neither is sorted. off_zone is sorted by app, because it comes from the roster rather than the zone.
Unlike set-all’s skipped, these rows carry no app: migrate reads the zone rather than the app roster, so a skipped subdomain need not name an app at all. reason is the vocabulary the two share.
JSON goes to stdout; banners and info messages go to stderr.