A private hosted zone is a Route 53 zone that answers only inside the VPCs you associate with it. It is how you give internal services stable names - db.internal, cache.svc.internal - without exposing anything to the public internet. Instances resolve those names to private IPs; the outside world gets nothing.
This is the one place Route 53's global model becomes region-aware: a private zone is bound to specific VPCs in specific regions, because a private name only means something inside a network.
Create a private zone by passing a VPC block (VPCId + VPCRegion) and setting PrivateZone: true in the HostedZoneConfig. The zone is live inside that VPC immediately - no registrar delegation, since it never touches public DNS.
You must include exactly one VPC when creating a private zone; you associate additional VPCs afterward. The VPCRegion is the region the VPC lives in, and it can differ from anything else in your setup - this is the region-scoping the global service otherwise lacks.
With the zone created, add records the same way as a public zone - a change batch of A records pointing at private IPs. Nothing about the record write changes; only who can resolve it does.
An EC2 instance in vpc-0abc123 now resolves db.internal.example to 10.0.1.20. An instance in an unassociated VPC, or anyone on the internet, gets NXDOMAIN. The private IPs never leak into public DNS.
Private DNS only works if the VPC has two attributes enabled: enableDnsSupport (the VPC provides the .2 Route 53 Resolver) and enableDnsHostnames. Without both, instances cannot resolve the private zone even though it exists. Check and set them with the EC2 client - this is the single most common reason a private zone "does not work."
Add another VPC - in the same or a different region - with AssociateVPCWithHostedZone. All associated VPCs then resolve the same private names, which is how you share internal DNS across a multi-VPC or multi-region footprint.
# --- Python (boto3) ---r53.associate_vpc_with_hosted_zone( HostedZoneId=zone_id, VPC={"VPCRegion": "eu-west-1", "VPCId": "vpc-0def456"}, Comment="share internal DNS with the EU VPC",)
// --- TypeScript (AWS SDK v3) ---import { AssociateVPCWithHostedZoneCommand } from "@aws-sdk/client-route-53";await r53.send(new AssociateVPCWithHostedZoneCommand({ HostedZoneId: zoneId, VPC: { VPCRegion: "eu-west-1", VPCId: "vpc-0def456" }, Comment: "share internal DNS with the EU VPC",}));
Cross-account association is a two-step handshake: the zone owner calls CreateVPCAssociationAuthorization, then the VPC owner (in the other account) calls AssociateVPCWithHostedZone. Remove a network later with DisassociateVPCFromHostedZone - you cannot remove the last remaining VPC.
You can create a private zone with the same name as a public one - say example.com both public and private. Inside an associated VPC, Route 53 answers from the private zone; outside, from the public zone. This split-horizon pattern lets api.example.com resolve to an internal IP for your services and a public IP for customers, with the same name.
VPC DNS attributes off.enableDnsSupport and enableDnsHostnames must both be on, or instances cannot resolve the private zone even though it exists.
Forgetting the required VPC on create.CreateHostedZone for a private zone must include exactly one VPC; omit it and the call fails.
Overlapping private zones. Two private zones with the same name associated to the same VPC create ambiguity; Route 53 uses the most specific match, so avoid overlap.
Cross-account single-step. Associating a VPC from another account needs CreateVPCAssociationAuthorization first; a direct associate call from the other account is denied.
Removing the last VPC. You cannot disassociate the final VPC from a private zone; delete the zone instead.
Expecting public resolution. A private zone never answers on the internet. If you need both, run split-horizon with a matching public zone.
AWS Cloud Map for richer service discovery with health-aware service instances and API-based lookup, built on Route 53 under the hood.
Route 53 Resolver endpoints and rules for hybrid DNS - forwarding between on-premises DNS and VPC-resolved private zones.
/etc/hosts or a config service for a handful of static internal names where a full private zone is overkill.
ECS/EKS service DNS (Cloud Map or CoreDNS) when discovery is scoped to a container orchestrator rather than the whole VPC.
Use a private hosted zone for VPC-wide internal names; reach for Cloud Map or Resolver rules when you need health-aware discovery or hybrid on-prem forwarding.
A Route 53 zone that answers DNS only inside the VPCs associated with it. Names resolve to private IPs for instances in those VPCs and are invisible to the public internet.
How do I create a private hosted zone?
Call CreateHostedZone with PrivateZone: true in HostedZoneConfig and exactly one VPC block (VPCId + VPCRegion). Associate additional VPCs afterward.
Why won't my instances resolve the private zone?
Almost always because the VPC's enableDnsSupport and enableDnsHostnames attributes are not both enabled. Set them with ModifyVpcAttribute on the EC2 client.
Can a private zone span multiple regions?
Yes. Associate VPCs from other regions with AssociateVPCWithHostedZone, passing each VPC's own VPCRegion. All associated VPCs resolve the same private names.
How do I share a private zone across accounts?
Two steps: the zone owner calls CreateVPCAssociationAuthorization, then the VPC owner in the other account calls AssociateVPCWithHostedZone. A single-step associate from the other account is denied.
What is split-horizon DNS?
Running a private and a public zone with the same name. Associated VPCs get the private answers, the internet gets the public ones - so a name can resolve internally and externally to different IPs.
Can I make a private zone public later?
No. Public versus private is fixed at creation. To switch, create a new zone of the other type and migrate the records.
Does a private zone need registrar delegation?
No. It never participates in public DNS, so there are no name servers to delegate to. It is live inside associated VPCs as soon as it is created.
Can I remove a VPC from a private zone?
Yes, with DisassociateVPCFromHostedZone, but not the last remaining VPC. To fully retire the zone, delete it instead.
Are private-zone records written differently?
No. You use the same ChangeResourceRecordSets change batches as a public zone. Only the resolution scope differs - private zones answer only inside associated VPCs.