home

Blog


Keeping DNS zones organized with zone delegation


DNS is one of those things that works so well in the background that nobody thinks about it until someone accidentally deletes an A record five minutes before a demo. As infrastructure grows and more environments show up, dumping every record for every environment into one giant hosted zone starts to feel a lot like that moment, just waiting to happen. Zone delegation is the fix, and it's simpler to set up than most people expect.

Organizing hosted zones in separate environments

Instead of having a single hosted zone holding records of all environments mixed together, each environment gets its own hosted zone, and the parent zone just points to them. So example.com stays the parent, while dev.example.com and others become their own independent zones that happen to live under the same domain.

This isn't just tidiness for the sake of tidiness. A smaller blast radius is the biggest win here: a bad record, a misconfigured TTL or someone fat-fingering a change in the dev zone can't touch anything in prod, because they're literally different zones with different record sets. It also makes IAM a lot more sane, since you can grant a team or a CI pipeline access to the dev zone alone, without handing over the keys to production DNS.

There's also a practical side for anyone using infrastructure as code. Since separate zones usually mean separate state, changes to one environment don't require locking or touching the state of another. And when something does go wrong, the change history for a single small zone is a lot easier to reason about than digging through a zone with hundreds of unrelated records.

Delegating a zone with Route53

Delegation itself is done through NS records. The parent zone doesn't need to know anything about the records inside the child zone, it just needs to know which name servers are authoritative for that subdomain and point to them with an NS record.

Creating the parent and child hosted zones

resource "aws_route53_zone" "parent" {
  name = "example.com"
}

resource "aws_route53_zone" "dev" {
  name = "dev.example.com"
}

Route53 automatically assigns a set of name servers to the new dev zone the moment it's created, you just need to grab them and hand them over to the parent.

Pointing the parent to the child through NS records

resource "aws_route53_record" "dev_delegation" {
  zone_id = aws_route53_zone.parent.zone_id
  name    = "dev.example.com"
  type    = "NS"
  ttl     = 172800

  records = aws_route53_zone.dev.name_servers
}

Once that applies, anything under dev.example.com gets resolved by asking the parent zone, which says "not my problem, go ask these name servers instead", and the child zone takes it from there. Any record you create inside the dev zone from that point on is completely isolated from the parent and from every other environment's zone.

Confirming the delegation worked

dig NS dev.example.com +short

If the output matches the name servers from the child zone, the delegation is working and you can move on with your life.

Wrap-up

Splitting hosted zones by environment and delegating them properly is a small amount of work that pays off the first time someone needs isolated permissions, a cleaner audit trail, or just wants to nuke and recreate a dev zone without holding their breath. It's one of those changes that feels unnecessary until the day it very much isn't.

Powered by Simple Blog API