← All case studies Amazon Route 53 → Cloudflare DNS

Two Route 53 zones moved to Cloudflare once tunnels were the reason to stay

Two personal domains left Route 53 for Cloudflare DNS on 13 January 2025. The bill was about 12 euros a year. The cutover took about 45 minutes, and Ansible then left stale records behind.

Ben Matheja wrote this up on 13 January 2025. The zones are mittbachweg.de and benmatheja.de. He buys domains at Netcup and points the nameservers wherever the records live. For years that was Amazon Route 53.

Route 53 had been fine for him. Resolution worked, Traefik completed Let’s Encrypt DNS-01 challenges, and Terraform owned the records. The bill was about €12 a year.

The reason to leave was Cloudflare Tunnel. His ISP uses carrier-grade NAT (DS-Lite), so he has no public IPv4 and no port forward. A tunnel was the first way he could expose the homelab. Once that traffic already entered through Cloudflare, a second DNS host stopped making sense. He puts the Cloudflare free plan at $0 a year.

This is a homelab, two zones, not a company outage. The Route 53 → Cloudflare DNS playbook still applies to the records and the delegation.

47 records, then a placeholder in the snippet

He exported with aws route53 list-resource-record-sets and found 47 records across both domains. Types were A, AAAA, CNAME, MX, and TXT.

The zone id in the published command is Z1234EXAMPLE, and several later snippets use example.com and 192.0.2.10. Those are stand-ins. The 47 and the two domain names are the figures he states as his.

Terraform created the Cloudflare zones on the free plan with jump_start off, so Cloudflare did not import records on its own. The provider constraint he prints is ~> 4.43.0.

Ansible kept records he had deleted

He also taught Ansible to create a Cloudflare record when a service deployed, through community.general.cloudflare_dns. That felt convenient until he removed a service.

Ansible does not track the record as state. Take the task out of the playbook and the DNS row stays. Deploy twice and he got duplicates. He cleaned those by hand in the Cloudflare UI.

The fix he landed on is boring. Static records, which are most of them, live in Terraform, including the tunnel CNAMEs. Ansible creates a record only when a deploy truly needs one. terraform apply deletes a record that left the config. Let’s Encrypt DNS-01 through Traefik still works after the move.

The nameserver change

He checked the new zone with dig against eva.ns.cloudflare.com, lowered the Route 53 TTL to 60 seconds, and updated the nameservers at Netcup. The new pair he names is eva.ns.cloudflare.com and walt.ns.cloudflare.com. He watched dig NS until it settled.

His result line is zero downtime, and full propagation in about 45 minutes.

If you copy the Ansible shortcut, plan the cleanup before the first deploy. A tunnel CNAME and a stale A record for the same name will fight, and Terraform will not see the row Ansible created. The playbook assumes one owner for the zone file. His post is what happens when a second tool writes into it.