Files
apix-mvp/docs/dns-migration-ionos-to-bunnynet.md
Carsten Rehfeld 46f32c2df2
Deploy to Production / deploy (push) Failing after 7s
chore: add missing source modules to version control
apix-demo, apix-portal/src, apix-spider/src, apix-registry/src,
apix-common/src were never staged. Without them the CI build has no
source to compile and the Docker images cannot be produced.

Also adds docs/ (infrastructure notes) missed in prior commits.

Co-Authored-By: Mira <noreply@anthropic.com>
2026-05-14 15:49:03 +02:00

8.6 KiB
Raw Permalink Blame History

DNS Migration: IONOS → BunnyDNS

Safe, zero-downtime migration of api-index.org from IONOS to BunnyDNS, enabling apex CNAME support for the Bunny.net CDN.

Risk profile: Low if the checklist is followed in order. The domain stays fully operational at every step. IONOS remains the authoritative fallback until you explicitly confirm the migration is complete.


Overview

Phase 0 — Audit        Inventory every record in IONOS. Nothing changes.
Phase 1 — Reduce TTL   Lower TTL to 300 s. Wait for old TTL to drain.
Phase 2 — Mirror       Replicate all records into BunnyDNS. Verify.
Phase 3 — Nameservers  Change NS at IONOS registrar → Bunny.net servers.
Phase 4 — CDN switch   Replace A record with CNAME to Bunny.net CDN edge.
Phase 5 — Cleanup      Confirm everything. IONOS zone stays intact as archive.

Total calendar time: 23 days minimum (TTL drain + propagation windows). You can compress to ~24 h if IONOS allows TTL = 60 s and you monitor closely.


Phase 0 — Audit Existing Records

Do this before touching anything.

Log in to IONOS → Domains & SSL → api-index.org → DNS.

Export or manually list every record. With no email on the domain the expected records are minimal:

Type Name Value Notes
A @ VPS IP or IONOS parking IP Migrate this
A / CNAME www same IP or alias Migrate this
CNAME _domainconnect IONOS internal Do NOT migrate — IONOS-specific

No MX, SPF, DKIM, or DMARC records exist (no email = no mail records). This makes the migration low-risk: there is nothing here that can cause an hours-long silent failure. The worst case is the website is briefly unreachable, which resolves as soon as you revert the nameservers.

Identify the current TTL for each record. IONOS default is often 3600 s (1 h). That is the minimum wait time after reducing TTL before the change is globally drained.


Phase 1 — Reduce TTL

Change every record's TTL in IONOS to 300 seconds (5 minutes).

IONOS UI path: DNS record → Edit → TTL field. If IONOS does not allow TTL below 3600 on your plan, use 3600 — it just means a longer wait in Phase 3.

After saving the reduced TTLs, wait for the old TTL to fully drain. If the old TTL was 3600 s (1 h), wait at least 1 hour before proceeding. This ensures no resolver is caching the old TTL value.

Verify the reduced TTL is live from an external resolver:

# Should show TTL=300 (or close to it) in the answer section
dig api-index.org A +noall +answer
dig api-index.org MX +noall +answer

Do not proceed to Phase 2 until the TTL shown in dig output matches your reduced value.


Phase 2 — Mirror Records into BunnyDNS

2a. Create the BunnyDNS zone

  1. Log in to Bunny.net → DNS → Add Zone
  2. Enter api-index.org and confirm
  3. Bunny.net assigns two nameservers (e.g. kiki.bunny.net, coco.bunny.net) — note these for Phase 3

2b. Add every record from Phase 0

Replicate the full record list into BunnyDNS exactly — same name, same value, same type. Use TTL = 300 for all records during migration.

Do not add:

  • _domainconnect — IONOS-internal, not needed
  • IONOS parking/redirect entries — not needed

Do add:

  • A record @ → VPS IP
  • A record (or CNAME) www → VPS IP (or api-index.org)

For the apex A record (@), add it pointing to the Hetzner VPS IP for now. You will convert it to the CDN CNAME in Phase 4 — not before.

2c. Verify the BunnyDNS zone before touching IONOS

Use dig with BunnyDNS as the explicit resolver to query the zone before nameserver delegation. Replace kiki.bunny.net with your assigned nameserver:

# A record — should return VPS IP
dig @kiki.bunny.net api-index.org A +short

# www
dig @kiki.bunny.net www.api-index.org A +short

Compare every answer against your Phase 0 audit. Do not proceed to Phase 3 until all records match.


Phase 3 — Switch Nameservers at IONOS

This is the single step that transfers authority. Once saved, resolvers will gradually start querying BunnyDNS instead of IONOS. IONOS DNS zone remains intact — you are only changing where resolvers are pointed, not deleting anything.

3a. Change nameservers in IONOS

IONOS UI path: Domains & SSL → api-index.org → Nameservers → Use custom nameservers

Enter the two nameservers from BunnyDNS (e.g.):

kiki.bunny.net
coco.bunny.net

Save. IONOS will show a warning that custom nameservers override IONOS DNS — confirm.

3b. Wait for propagation

Propagation is complete when global resolvers return the BunnyDNS IP for your domain. This typically takes 1560 minutes with TTL=300. It can take up to 48 hours in rare cases (resolvers ignoring low TTLs). Monitor:

# Repeat every few minutes — watch for the IONOS nameservers to disappear
dig api-index.org NS +short

# Check from multiple global vantage points
# https://dnschecker.org/#A/api-index.org — paste in browser, check all green

During propagation: Some resolvers still use IONOS, some use BunnyDNS. Both zones have identical records pointing to the VPS IP — so the site stays up regardless of which nameserver a resolver hits.

3c. Verify after propagation

# NS records should now show Bunny.net nameservers from all resolvers
dig api-index.org NS +short

# A record resolves to VPS IP
dig api-index.org A +short

# End-to-end HTTPS
curl -sv https://api-index.org/ 2>&1 | grep -E "HTTP|certificate"

Do not proceed to Phase 4 until all checks pass.


Phase 4 — Switch Apex Record to CDN CNAME

Only do this after Phase 3 is fully confirmed.

4a. Run the Bunny.net CDN setup script

If not already done, provision the pull zone:

BUNNYNET_API_KEY=your-key \
ORIGIN_URL=https://<vps-ip>   \
CUSTOM_HOSTNAME=api-index.org \
SYSLOG_HOST=<vps-ip>          \
INSTALL_CRON=true             \
./scripts/setup-bunnynet.sh

Note the CDN hostname printed at the end (e.g. apix-registry.b-cdn.net).

4b. Replace the apex A record with a CNAME in BunnyDNS

In BunnyDNS → api-index.org zone:

  1. Delete the @ A record pointing to the VPS IP
  2. Add a CNAME record: @apix-registry.b-cdn.net (your CDN hostname)

BunnyDNS supports CNAME at the apex via automatic flattening — this is why you migrated here.

4c. Verify CDN is serving the domain

# A record now resolves to Bunny.net edge IP (not VPS IP)
dig api-index.org A +short

# HTTPS still works
curl -sv https://api-index.org/ 2>&1 | grep HTTP

# Second request should be a CDN cache HIT
curl -sI "https://api-index.org/services?capability=nlp" | grep -i cache

Phase 5 — Cleanup and Archive

Do not delete the IONOS DNS zone. Leave it intact as a ready-to-activate fallback for at least 30 days. If something goes wrong after Phase 3, you can revert by changing the nameservers back to IONOS in the registrar — the zone still has all the correct records.

After 30 days of stable operation:

  • IONOS zone can be deleted or left (it costs nothing to keep)
  • Document the Bunny.net nameservers in .env or docs/ for future reference

Rollback Procedure

At any phase before Phase 3 is complete: nothing has changed — no rollback needed.

After Phase 3 (nameserver switch):

IONOS → Domains → api-index.org → Nameservers → Use IONOS nameservers

Propagation back takes 560 minutes with TTL=300. The IONOS zone was never modified — it still has all records.

After Phase 4 (CNAME switch):

  1. In BunnyDNS: delete the CNAME @, re-add the A record @ → VPS IP
  2. The CDN is bypassed; traffic flows directly to VPS again

Checklist Summary

Phase 0
  [ ] Audit and export all records from IONOS (expected: A @ , A/CNAME www, _domainconnect)
  [ ] Identify current TTL values
  [ ] Confirm no MX records present (no email on domain)

Phase 1
  [ ] Reduce all TTLs to 300 s in IONOS
  [ ] Wait for old TTL to drain (at minimum the old TTL duration)
  [ ] dig confirms TTL ~300 in answers

Phase 2
  [ ] BunnyDNS zone created for api-index.org
  [ ] A record @ and www replicated into BunnyDNS (no MX/TXT to worry about)
  [ ] Verified via: dig @<bunny-ns> api-index.org A  and  dig @<bunny-ns> www.api-index.org A

Phase 3
  [ ] Nameservers changed at IONOS registrar to BunnyDNS
  [ ] Propagation monitored until global NS shows Bunny.net
  [ ] HTTPS end-to-end test passes

Phase 4
  [ ] CDN pull zone provisioned (setup-bunnynet.sh)
  [ ] Apex A record replaced with CNAME in BunnyDNS
  [ ] CDN cache HIT confirmed on second request

Phase 5
  [ ] IONOS zone archived (do not delete for 30 days)
  [ ] Bunny.net nameservers documented