Route 53 Subdomain Delegation: Cross-Account DNS and ACM Certificate Verification

2 min readCloud Infrastructure

Subdomain delegation hands off DNS authority for a subdomain to a separate hosted zone — in the same or a different AWS account. The child zone's name servers go into the parent zone as NS records. Certificate validation for the delegated subdomain happens in the child zone, not the parent.

awsroute53dnsacm

How DNS delegation works

DNS is a hierarchy. The root zone delegates authority for .com to Verisign's name servers. Verisign delegates example.com to your Route 53 hosted zone. Your hosted zone can delegate api.example.com to another hosted zone (in any account) by adding NS records pointing to the child zone's name servers.

When a resolver queries api.example.com:

  1. Root → .com name servers
  2. .comexample.com name servers (your main hosted zone)
  3. example.com → sees NS records for api.example.com → redirects to child zone name servers
  4. Child zone → returns the actual answer

The NS record delegation chain

ConceptRoute 53 / DNS

Delegation is implemented by adding NS records to the parent zone that point to the child zone's name servers. These NS records tell resolvers: 'for anything under api.example.com, ask these four name servers.' The child zone has complete authority over all records under that subdomain.

Prerequisites

  • DNS hierarchy and zones
  • Route 53 hosted zones
  • NS records

Key Points

  • Create the child hosted zone first — Route 53 assigns it four name servers.
  • Add those four name servers as NS records in the parent zone.
  • The delegation is purely DNS — it doesn't matter which AWS account the child zone is in.
  • Once delegated, records under api.example.com must be in the child zone (parent zone records for that subdomain are unreachable).

Cross-account subdomain delegation

The most common production pattern: a root account owns the apex domain, and each environment or team gets a subdomain in their own account.

Step 1: Create the child hosted zone in the child account

# In account B (api team)
aws route53 create-hosted-zone \
  --name api.example.com \
  --caller-reference unique-string-$(date +%s)

# Note the four Name Server values returned
# Example: ns-123.awsdns-45.com, ns-456.awsdns-67.net, ...
# Terraform in account B
resource "aws_route53_zone" "api" {
  name = "api.example.com"
}

output "name_servers" {
  value = aws_route53_zone.api.name_servers
}

Step 2: Add NS records to the parent zone in the root account

# Terraform in account A (root account)
resource "aws_route53_record" "api_delegation" {
  zone_id = aws_route53_zone.root.zone_id
  name    = "api.example.com"
  type    = "NS"
  ttl     = 300

  records = [
    "ns-123.awsdns-45.com.",    # note trailing dot — required for FQDN
    "ns-456.awsdns-67.net.",
    "ns-789.awsdns-01.org.",
    "ns-012.awsdns-34.co.uk.",
  ]
}

After this, any records added to the api.example.com hosted zone in account B resolve correctly. Account B has full DNS authority over the subdomain.

ACM certificate validation after delegation

TLS certificate validation via DNS (the AWS recommended method) requires adding a CNAME record that ACM specifies. After delegation, this CNAME must go in the child zone, not the parent.

Certificate request: *.api.example.com in account B
ACM says: add CNAME _abc123.api.example.com → _def456.acm-validations.aws.

Where to add it: api.example.com hosted zone in account B

This is correct because api.example.com authority belongs to account B's zone after delegation. Adding the validation record to account A's zone won't work — resolvers won't find it there because account A's NS records for api.example.com point to account B.

# In account B — both the certificate and its validation record
resource "aws_acm_certificate" "api" {
  domain_name       = "*.api.example.com"
  validation_method = "DNS"
}

resource "aws_route53_record" "api_cert_validation" {
  for_each = {
    for dvo in aws_acm_certificate.api.domain_validation_options : dvo.domain_name => {
      name   = dvo.resource_record_name
      record = dvo.resource_record_value
      type   = dvo.resource_record_type
    }
  }

  zone_id = aws_route53_zone.api.zone_id   # api.example.com zone in account B
  name    = each.value.name
  type    = each.value.type
  ttl     = 60
  records = [each.value.record]
}

resource "aws_acm_certificate_validation" "api" {
  certificate_arn         = aws_acm_certificate.api.arn
  validation_record_fqdns = [for record in aws_route53_record.api_cert_validation : record.fqdn]
}
💡Verifying delegation works: dig commands

After setting up delegation, verify the chain works before troubleshooting application issues:

# Check what NS records the parent zone returns for the subdomain
dig NS api.example.com @8.8.8.8

# Should return the child zone's name servers, not the parent's
# Expected: ns-123.awsdns-45.com., ns-456.awsdns-67.net., ...

# Check that the child zone answers authoritatively
dig A something.api.example.com @ns-123.awsdns-45.com

# Check the full resolution chain
dig +trace api.example.com

# Verify ACM validation record is in the child zone
dig CNAME _abc123.api.example.com

Common failure mode: the NS record in the parent zone uses the wrong name servers (copied incorrectly or from a different hosted zone). Use dig NS api.example.com to confirm the returned name servers match the child zone's actual name servers.

Another common failure: the child hosted zone has been recreated (deleted and re-created), generating new name servers. The parent zone NS record still points to the old name servers. This breaks resolution silently — the old name servers are AWS-controlled but no longer authoritative for this zone.

You delegate api.example.com from account A to account B. In account B, you add a CNAME for www.api.example.com and an A record for api.example.com itself. DNS resolution for www.api.example.com works but api.example.com returns NXDOMAIN. What is wrong?

medium

The NS delegation is correct — resolvers find the child zone. The CNAME for www works. The A record for api.example.com (the subdomain apex) was added to the child hosted zone in account B.

  • AA records for zone apex subdomains must be added to the parent zone
    Incorrect.After delegation, all records under api.example.com — including the apex api.example.com itself — belong in the child zone. The parent zone's authority stops at the NS delegation.
  • BThe child zone's name servers have SOA and NS records for api.example.com which conflict with the A record
    Incorrect.NS and SOA records at the zone apex of the child zone are required by DNS and don't conflict with A records. A and NS can coexist at the zone apex.
  • Capi.example.com in account B's hosted zone has only NS and SOA records by default — you need to check if the A record was actually saved correctly in the correct hosted zone
    Correct!The most likely cause is that the A record was accidentally added to account A's example.com zone (where api.example.com appears as a name) instead of account B's api.example.com zone. After delegation, resolvers query account B's name servers for api.example.com — they won't see records added to account A's zone. Verify the A record exists in account B's hosted zone, not account A's.
  • DThe A record at the zone apex of a delegated subdomain requires an Alias record, not a plain A record
    Incorrect.Plain A records work at the zone apex of a hosted zone. Alias is only required when pointing to AWS resources without a static IP, or when you need zero-cost DNS queries.

Hint:www.api.example.com works (it's in the child zone). api.example.com doesn't. Where might the A record have been accidentally placed?