24.1 Route 53 Hosted Zones: Public and Private
Alright, let’s talk about Hosted Zones, the bedrock of everything you do in Route 53. Think of them less as a “zone” and more as a container for all the DNS records for a specific domain. It’s the official, authoritative ledger for your domain’s internet presence, managed by AWS instead of some crusty old web portal from your registrar. Route 53 comes in two distinct flavors: Public and Private. Picking the wrong one is like trying to use your car keys to open your front door—frustrating and ultimately a sign you’ve misunderstood the fundamental nature of the thing.
Public Hosted Zones: Your Global Phone Book
A public hosted zone is what you use for domains that need to be resolved from the public internet. example.com, myapp.io, you know the drill. When you create one and populate it with records (like an A record pointing your www subdomain to an Application Load Balancer), Route 53 becomes the authority for that domain. You’ll then need to go to your domain registrar (could be Amazon, GoDaddy, whoever) and update the domain’s Name Servers (NS) to the set of four authoritative name servers that Route 53 automatically assigns your hosted zone. This is called delegation, and it’s just you telling the global DNS system: “Hey, for any questions about example.com, go ask these four servers over at AWS. They’ve got the list.”
Why would you do this? Control, speed, and integration. You can manage your DNS records right alongside your AWS infrastructure using IAM, CloudFormation, and the CLI. It’s all in one place.
Here’s a quick CLI command to create one. Notice the CallerReference, which needs to be unique. A timestamp is the classic way to go.
aws route53 create-hosted-zone \
--name "example.com" \
--caller-reference "$(date +%s)"
The juicy part of the response is the set of name servers. You’ll need those.
{
"HostedZone": {
"Id": "/hostedzone/Z123456789EXAMPLE",
"Name": "example.com.",
"CallerReference": "1720970066",
"Config": {
"Comment": "",
"PrivateZone": false
},
"ResourceRecordSetCount": 2
},
"DelegationSet": {
"NameServers": [
"ns-123.awsdns-45.com.",
"ns-6789.awsdns-01.org.",
"ns-2345.awsdns-67.co.uk.",
"ns-890.awsdns-12.net."
]
}
}
Private Hosted Zones: Your Internal Company Directory
Now, this is where it gets interesting. A private hosted zone is for domains that should only be resolvable from within one or more specified Amazon VPCs. You’d use this for your internal services: database.prod.internal, auth-api.dev.internal, that sort of thing.
This is a killer feature. It means you can have a seamless, human-readable DNS structure inside your private cloud network without ever leaking those names to the public internet. It’s a huge security and operational win. The magic that makes this work is the association you create between the hosted zone and your VPC(s). You’re essentially telling Route 53: “Only DNS resolvers within these specific VPCs are allowed to ask questions about this internal domain.”
Creating one is almost identical, but you must specify the VPC ID and AWS region.
aws route53 create-hosted-zone \
--name "internal.example.com" \
--vpc VPCRegion="us-east-1",VPCId="vpc-1a2b3c4d" \
--caller-reference "$(date +%s)"
Notice the response now lacks a DelegationSet (there are no public name servers to give you), and the PrivateZone flag is set to true.
The Critical Difference and The Gotcha
The fundamental rule is simple: Public zones are for public resolution, private zones are for resolution within your VPCs. But here’s the classic head-scratcher, the thing that trips up everyone at least once:
You can have both a public and a private hosted zone for the exact same domain name.
Let that sink in. You can have a public zone for example.com managing your website and a private zone also for example.com inside your VPC. Why would you do this? The most common reason is split-view DNS. Imagine you have a web application at app.example.com. For users on the public internet, you want that to resolve to the public IP of your load balancer. But for your own EC2 instances inside the VPC, you want that exact same name, app.example.com, to resolve to the internal IP of that same load balancer. This is fantastic for reducing latency and keeping internal traffic… internal.
The resolver inside your VPC is smart enough to check the private hosted zone first. If it finds a record there, it uses it. If not, it falls back to the public internet. So your VPC resources get the internal address, and everyone else gets the external one. It’s elegant, but my god, it can be confusing to debug if you forget you’ve set it up. Always check both your public and private zones when DNS isn’t behaving as expected.