AWS Certified CloudOps Engineer - Associate
Route 53 DNS and Resolver
A name that resolves from your laptop and returns NXDOMAIN inside a VPC is not a broken record, it is a different resolver answering. This lesson covers hosted zones, alias records, private hosted zones, and the inbound and outbound Resolver endpoints that make hybrid name resolution work.
- Explain what a hosted zone is and distinguish a public hosted zone from a private hosted zone
- Choose between an alias record and a CNAME record for a given target, including at the zone apex
- Configure a private hosted zone, including the 2 VPC attributes it depends on, and predict the NXDOMAIN behavior of overlapping namespaces
- Describe how the VPC Resolver at VPC+2 answers queries for local names, private hosted zones, and public names
- Select an inbound or outbound Resolver endpoint for a given direction of hybrid DNS traffic
- Compare public DNS query logging with Resolver query logging and pick the right one for an investigation
A Lambda function in your VPC calls payments.corp.internal and gets NXDOMAIN. The same name resolves in 30 milliseconds from your laptop over the corporate VPN. Nothing in the application changed, the record exists, and DNS is "working" by every check you can think of.
The record is fine. What changed is which resolver answered. A name inside a VPC can be resolved by 3 different things, and they do not consult each other. Learning where a query goes is most of Route 53 troubleshooting, and it is what the exam actually tests in this area.
Hosted zones: the container Route 53 answers from
A hosted zone holds the records for one domain and its subdomains. Create a hosted zone for example.com and you get a container that Route 53 will answer queries from, plus the name servers that make that answering official.
There are 2 kinds, and the difference is who can ask:
- A public hosted zone answers queries from the internet. Route 53 assigns 4 name servers, and you point your domain registrar at them with NS records. That delegation is what makes Route 53 authoritative for the domain.
- A private hosted zone answers queries only from VPCs you associate with it. It is not reachable from the internet, and its records never appear in public DNS.
Private hosted zones do get NS records, because the DNS protocol requires every zone to have them, and they are always the same 4 reserved names:
ns-0.awsdns-00.com
ns-512.awsdns-00.net
ns-1024.awsdns-00.org
ns-1536.awsdns-00.co.uk
Those names are visible on the internet, but querying them directly returns nothing about your zone. The VPC Resolver never contacts them either. It recognizes that a query falls inside a private namespace from the VPC-to-hosted-zone association and reaches the private data directly. So "the name servers are public" is true and harmless.
Records, TTL, and the query you never see
A record maps a name and a type to a value: www.example.com type A to 203.0.113.10. Each record carries a TTL, the number of seconds a DNS resolver may cache the answer before asking again.
The TTL is the single most operationally important field on a record, and it is easy to underestimate. Set it to 300 and a resolver that answered a client at 10:00:00 will keep serving that same answer until 10:05:00 without contacting Route 53 at all. That has 3 consequences you will meet again in this topic:
- A failover or a record change does not take effect for existing clients until their cached copy expires.
- Query logs undercount real traffic, sometimes by orders of magnitude, because cached answers never reach Route 53.
- Lowering the TTL before a planned cutover, then raising it afterwards, is the standard migration technique.
Alias records: the AWS-only shortcut
You want example.com to reach an Application Load Balancer. The load balancer has a DNS name, not a stable IP address, so an A record with a hardcoded address is wrong the day AWS replaces a node. The obvious answer is a CNAME, and the DNS protocol forbids it: you cannot create a CNAME at the zone apex, the top node of the namespace.
An alias record is Route 53's extension that closes this gap. It looks like an A or AAAA record to any client, but its target is an AWS resource that Route 53 resolves at query time.
| Alias record | CNAME record | |
|---|---|---|
| Target | Selected AWS resources, or another record in the same hosted zone | Any DNS name anywhere |
| At the zone apex | Allowed | Not allowed |
| TTL | Taken from the target resource, not settable by you | You set it |
| Price | Free for queries to AWS resources | Billed, and billed as 2 queries when it points at another Route 53 record |
| Type matching | Answers only when the query type matches the record type | Redirects regardless of the type queried |
In dig output | Appears as A or AAAA | Appears as CNAME |
Alias targets you should recognize: Elastic Load Balancing load balancers (Application, Network, and Classic), CloudFront distributions, S3 buckets configured as static websites, API Gateway APIs, VPC interface endpoints, Global Accelerator accelerators, Elastic Beanstalk environments, App Runner services, AppSync domain names, OpenSearch Service domains, and another record of the same type in the same hosted zone.
Two practical points. Alias records track the resource on their own, so if the load balancer's addresses change, Route 53 starts answering with the new ones without you touching anything. And alias records can set Evaluate Target Health, which is how a failover configuration learns that a load balancer is down without you creating a health check for it.
Private hosted zones and the 2 VPC attributes they depend on
Create a private hosted zone, associate a VPC, and add records. Instances in that VPC now resolve those names to private addresses.
It fails silently if the VPC is not configured for it. Both of these VPC attributes must be true:
enableDnsSupportenableDnsHostnames
aws ec2 describe-vpc-attribute --vpc-id vpc-0a1b2c3d --attribute enableDnsSupport
aws ec2 describe-vpc-attribute --vpc-id vpc-0a1b2c3d --attribute enableDnsHostnames
aws ec2 modify-vpc-attribute --vpc-id vpc-0a1b2c3d --enable-dns-hostnames
A private hosted zone can be associated with up to 300 VPCs, across accounts (the association from another account is a 2-step authorize-then-associate flow). Past 300, Route 53 Profiles is the intended tool rather than a quota increase.
Health checks behave differently here. In a private hosted zone you can attach health checks only to failover, multivalue answer, weighted, latency, geolocation, and geoproximity records. IP-based routing is not supported in a private hosted zone at all.
Split-view DNS and the NXDOMAIN trap
Split-view DNS (also called split-horizon) is the pattern where the same domain name resolves differently inside and outside your network. You create a public hosted zone and a private hosted zone with the same name, associate the private one with your VPCs, and populate each with the records appropriate to its audience. Internal callers reach 10.0.5.20; the internet reaches your CloudFront distribution.
Here is the part that surprises people. Suppose your private hosted zone is example.com and it contains records for db.example.com and cache.example.com, but nothing for reports.example.com, which does exist in public DNS.
The VPC Resolver evaluates in this order:
- Does a private hosted zone associated with this VPC match the query name? A match means an identical name, or a name that is a parent of the query.
example.comis a parent ofreports.example.com, so yes. - Search that zone for a record matching the name and type.
- No match. Return NXDOMAIN.
It does not fall back to public DNS. Once a private zone claims the namespace, it owns the whole namespace inside that VPC. This is the second most common private hosted zone incident after the missing VPC attributes, and the symptom is always the same: one name works everywhere except inside the VPC.
When 2 private hosted zones overlap, the most specific match wins. With zones for example.com and accounting.example.com both associated, a query for seattle.accounting.example.com is answered from accounting.example.com, and only from that zone.
The VPC Resolver at VPC+2
Every VPC gets a resolver at the base of its CIDR plus two. A VPC using 10.20.0.0/16 has its resolver at 10.20.0.2. That address is one of the 5 AWS reserves in every subnet, and it is where instances send DNS queries unless a DHCP options set says otherwise.
The VPC Resolver answers 3 categories:
- Internal EC2 hostnames such as
ip-10-20-1-15.ec2.internal - Records in private hosted zones associated with the VPC
- Public names, by performing recursive lookups against public name servers on your behalf
That third one matters: instances do not talk to public DNS themselves, the VPC Resolver does the recursion. It can also validate DNSSEC on those recursive answers if you enable DNSSEC validation.
If you run your own DNS servers on EC2 instances, they must forward to VPC+2 to reach private hosted zone data. Pointing them at the VPC router (.1) does not work.
The AWS docs now call this component Route 53 VPC Resolver; older material and the exam guide call it Route 53 Resolver. They are the same thing.
Hybrid DNS: inbound and outbound endpoints
The VPC Resolver knows nothing about corp.internal running on your on-premises Active Directory servers, and your on-premises resolver knows nothing about your private hosted zones. Resolver endpoints bridge the two, and their names are the single most reversed pair of terms in this domain.
Both names are written from the VPC's point of view:
- An outbound endpoint sends queries out of the VPC to your network. You pair it with forwarding rules, one per domain name, that say "queries for
corp.internalgo to these target IP addresses." - An inbound endpoint accepts queries into the VPC from your network. You give its IP addresses to your on-premises resolver as a conditional forwarder.
An endpoint is a set of elastic network interfaces placed in subnets you choose, so it consumes private addresses from your VPC. Two consequences follow:
- An outbound endpoint's IP addresses are private, so the query can only reach your data center over Direct Connect, a Site-to-Site VPN, or a NAT gateway. There is no public path.
- Every target IP address in a rule must be reachable from the endpoint's subnets. When Resolver forwards a query it picks one target IP at random, with no preference, and retries against another random target if the first does not answer. One unreachable target in a list of 3 therefore produces intermittent slow resolution rather than a clean failure.
aws route53resolver create-resolver-endpoint \
--name outbound-to-datacenter \
--direction OUTBOUND \
--security-group-ids sg-0a1b2c3d4e5f6a7b8 \
--ip-addresses SubnetId=subnet-0aaa,SubnetId=subnet-0bbb
aws route53resolver create-resolver-rule \
--name forward-corp-internal \
--rule-type FORWARD \
--domain-name corp.internal \
--resolver-endpoint-id rslvr-out-0123456789abcdef0 \
--target-ips Ip=192.168.10.53,Port=53 Ip=192.168.20.53,Port=53
A rule does nothing until you associate it with a VPC. Rules are per-Region resources and can be shared with other accounts through AWS RAM, which is the standard pattern: one networking account owns the outbound endpoint and the rules, and every workload account associates the shared rules with its VPCs.
When a rule and a private hosted zone disagree
You have a private hosted zone for corp.internal and someone adds a forwarding rule for corp.internal on the same VPC. Which answers?
The Resolver rule wins. Queries are forwarded to your network, and the records sitting in the private hosted zone are never consulted.
This is worth naming out loud because the failure is invisible from the Route 53 console: the zone is there, the records are there, the association is there, and none of it is used. If you need both, scope the rule to a narrower name than the zone, or drop the association.
Query logging: 2 different logs
Skill 5.2.2 names query logging directly, and there are 2 separate features with the same word in the name. Picking the wrong one wastes an investigation.
| Public DNS query logging | Resolver query logging | |
|---|---|---|
| What it captures | Queries that DNS resolvers send to Route 53 for a public hosted zone you own | Queries originating in VPCs you specify, queries arriving through an inbound endpoint, queries leaving through an outbound endpoint, and DNS Firewall rule actions |
| Scope | Per public hosted zone, 1 configuration each | Per VPC, associated to a configuration |
| Destination | CloudWatch Logs only, and the log group must be in us-east-1 | CloudWatch Logs, an S3 bucket, or a Firehose delivery stream |
| Identifies the client | The resolver's IP address, plus a truncated EDNS client subnet when the resolver sends one | The VPC ID, the instance ID, and the instance's IP address |
| Log stream naming | {hosted-zone-id}/{edge-location-id}, for example Z1D633PJN98FT9/DFW3 | Standard CloudWatch Logs streams |
| Route 53 charge | None (you pay CloudWatch Logs) | None (you pay the destination) |
A public query log entry looks like this:
1.0 2026-08-11T08:16:02.130Z Z123412341234 example.com A NOERROR UDP DFW3 192.0.2.10 198.51.100.0/24
Version, timestamp, hosted zone ID, query name, query type, response code, layer 4 protocol, edge location, resolver IP, EDNS client subnet.
Both logs are shaped by caching, and both undercount for the same reason. Public query logs miss everything a downstream resolver served from its own cache. Resolver query logs record only unique queries: if an instance asks for accounting.example.com twice inside the VPC Resolver's cache TTL, the second lookup never appears. Reading either log as a request counter will mislead you.
Quotas worth remembering
| Limit | Value |
|---|---|
| Hosted zones per account | 500 (adjustable) |
| Records per hosted zone | 10,000 (adjustable, extra charge above 10,000) |
| VPCs associated with one private hosted zone | 300 |
| Active health checks per account | 200 (adjustable) |
| Child health checks per calculated health check | 255 |
| Resolver endpoints per Region | 4 per account (adjustable) |
| IP addresses per Resolver endpoint | 6 (adjustable) |
| Target IP addresses per Resolver rule | 6 |
| Resolver rules per Region | 1,000 (adjustable) |
| Rule-to-VPC associations per Region | 2,000 (adjustable) |
| UDP queries per second per endpoint IP address | 10,000, dropping to as low as 1,500 when connection tracking is enforced or queries arrive through a Network Load Balancer |
| Query logging configurations per hosted zone | 1 |
Exam tips
- "Point the apex at a load balancer, a CloudFront distribution, or an S3 website bucket" is always an alias record. A CNAME at the apex is never a valid answer.
- Alias queries to AWS resources are free; CNAME queries are billed, and a CNAME pointing at another Route 53 record is billed twice. Cost-flavored DNS questions usually resolve to alias.
- A private hosted zone that resolves nothing means
enableDnsSupportandenableDnsHostnames. Check them before anything else. - "Works everywhere except inside the VPC, and the name is a subdomain of a private hosted zone" is the NXDOMAIN trap. There is no fallback to public DNS.
- Inbound and outbound are named from the VPC's perspective. On-premises clients resolving AWS names need an inbound endpoint. AWS instances resolving on-premises names need an outbound endpoint plus a forwarding rule.
- A Resolver rule beats a private hosted zone for the same domain name.
- Public query logging is us-east-1 only and covers public hosted zones. Attributing a lookup to an instance requires Resolver query logging.
- VPC+2 is the resolver address. VPC+1 is the router. Custom DNS servers forward to +2.
The rule to carry forward: before you debug a record, work out which resolver answered. A private hosted zone, a Resolver rule, and public DNS all claim names, they win in that reverse order (rule, then zone, then public), and none of them falls back to the next once it has claimed the namespace. With that settled, the next lesson changes the question from who answers to which of several answers you get, which is what routing policies decide.
