DNS Inside a VPC
Every resource inside a Virtual Private Cloud (VPC, your own private network in AWS) needs a way to turn names like db.internal or s3.amazonaws.com into IP addresses. That job is done by DNS (the Domain Name System, the internet’s “phone book”). AWS gives every VPC a built-in DNS resolver, but two small settings control how well it works — and getting them wrong is one of the most common reasons a custom VPC behaves strangely. This page explains how VPC DNS works and how to configure it correctly.
The Amazon-provided DNS resolver
When you create a VPC, AWS automatically runs a DNS resolver for you. This resolver is sometimes called the Amazon-provided DNS or the Route 53 Resolver. You reach it at a special, reserved IP address: the base of your VPC CIDR block plus two.
So if your VPC uses the range 10.0.0.0/16, the resolver lives at 10.0.0.2. There is also a fixed link-local address that always works from any VPC: 169.254.169.253.
This resolver does several jobs:
- Resolves public internet names (like
www.google.com) for instances that can reach the internet. - Resolves the internal AWS records for your instances (their private DNS names).
- Resolves names in any private hosted zone you attach to the VPC (more on that below).
- Resolves the special private DNS names used by VPC interface endpoints.
The
.2resolver address only answers queries that come from inside the VPC. You cannot query it from your laptop at home or from another VPC unless you set up a Route 53 Resolver inbound endpoint.
The two settings that control everything
VPC DNS behaviour is controlled by two attributes on the VPC. They are easy to overlook because they are buried in the VPC settings, but they decide whether your instances get DNS names at all.
| Attribute | What it does | Default in your custom VPC |
|---|---|---|
enableDnsSupport | Turns the Amazon resolver at .2 on or off. When off, instances cannot resolve any DNS names through AWS. | true (enabled) |
enableDnsHostnames | Decides whether instances with a public IP receive a public DNS hostname (like ec2-203-0-113-25.compute-1.amazonaws.com). | false (disabled) |
Here is the gotcha that trips up almost everyone:
An instance only gets a public DNS hostname when BOTH
enableDnsHostnamesANDenableDnsSupportare turned on. The default VPC has both set totrue, so it “just works”. A VPC you create yourself hasenableDnsHostnamesset tofalse, so your instances launch with a public IP but no public DNS name. This is the number-one reason custom VPCs “resolve oddly”.
These same two flags also gate interface endpoints. If you enable a VPC interface endpoint (for example, a private endpoint to reach Amazon S3 or Secrets Manager without using the internet) and turn on its private DNS, it will silently fail to resolve unless both DNS settings are on.
Checking and changing the settings (Console)
- Open the VPC console and choose Your VPCs in the left menu.
- Select your VPC (for example
vpc-0a1b2c3d). - Choose Actions → Edit VPC settings.
- Tick Enable DNS resolution (this is
enableDnsSupport). - Tick Enable DNS hostnames (this is
enableDnsHostnames). - Choose Save.
Checking and changing the settings (CLI)
Check the current values:
aws ec2 describe-vpc-attribute \
--vpc-id vpc-0a1b2c3d \
--attribute enableDnsHostnames
Output:
{
"VpcId": "vpc-0a1b2c3d",
"EnableDnsHostnames": {
"Value": false
}
}
Turn both attributes on (they must be set one at a time):
aws ec2 modify-vpc-attribute \
--vpc-id vpc-0a1b2c3d \
--enable-dns-support
aws ec2 modify-vpc-attribute \
--vpc-id vpc-0a1b2c3d \
--enable-dns-hostnames
These commands print no output on success.
Setting it in infrastructure as code
If you build VPCs with Terraform, set both flags when you create the VPC so you never hit the gotcha:
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "main-vpc"
}
}
Private hosted zones for internal names
A private hosted zone is a Route 53 DNS zone that is only visible inside the VPCs you attach it to. It lets you use friendly internal names — like db.prod.internal or cache.prod.internal — instead of memorising private IP addresses. The names never leak to the public internet.
When to use this: when services inside your VPC need stable, human-readable names for each other (databases, internal APIs, caches), and you want those names hidden from the outside world. When NOT to: for names that must be reachable from the public internet — those belong in a public hosted zone.
Creating a private hosted zone (Console)
- Open the Route 53 console and choose Hosted zones.
- Choose Create hosted zone.
- Enter a Domain name, for example
prod.internal. - Under Type, choose Private hosted zone.
- Choose the Region and select your VPC (
vpc-0a1b2c3d). - Choose Create hosted zone, then add records (for example an
Arecorddb.prod.internal → 10.0.1.50).
Creating a private hosted zone (CLI)
aws route53 create-hosted-zone \
--name prod.internal \
--vpc VPCRegion=us-east-1,VPCId=vpc-0a1b2c3d \
--caller-reference "prod-internal-$(date +%s)" \
--hosted-zone-config PrivateZone=true
Output:
{
"HostedZone": {
"Id": "/hostedzone/Z0123456789ABCDEFGHIJ",
"Name": "prod.internal.",
"Config": {
"PrivateZone": true
},
"ResourceRecordSetCount": 2
},
"VPC": {
"VPCRegion": "us-east-1",
"VPCId": "vpc-0a1b2c3d"
}
}
A private hosted zone also requires enableDnsSupport and enableDnsHostnames to be true on the VPC, or the names will not resolve from your instances.
Cost note: the Amazon-provided resolver and standard query resolution are free. A private hosted zone costs about $0.50 per hosted zone per month, and DNS queries are billed at roughly $0.40 per million queries — effectively pennies for most workloads. Route 53 Resolver inbound/outbound endpoints cost more (around $0.125 per ENI per hour), so only add those when you need hybrid DNS with on-premises networks.
Best Practices
- Always set both
enableDnsSupportandenableDnsHostnamestotruewhen you create a custom VPC — bake it into your Terraform or CloudFormation so you never debug it later. - Use private hosted zones for internal service names instead of hard-coding private IPs, so addresses can change without breaking callers.
- Remember the resolver lives at VPC CIDR base + 2 (and
169.254.169.253); keep firewall and NACL rules from blocking UDP/TCP port 53 to it. - Enable both DNS settings before turning on private DNS for interface endpoints, or the endpoint names will silently fail to resolve.
- Keep internal domains under a clearly private suffix (like
.internal) so they never collide with real public domains. - Use VPC Flow Logs when DNS oddities persist — they help confirm whether traffic is even reaching the resolver.