Implementing Private DNS Resolution for AWS VPC: Route53 vs Custom Solutions


1 views

When working with AWS VPCs, one persistent challenge is maintaining reliable internal DNS resolution. Traditional approaches like manually updating /etc/hosts files or running custom DNS servers (MaraDNS, BIND) create operational overhead and fragility.

Consider this common scenario: Your application servers need to connect to a database cluster. Hardcoding IP addresses creates maintenance headaches:

# Bad practice - IP-based connection
db_connection = "jdbc:mysql://10.0.1.25:3306/mydb"

# Better - DNS-based connection
db_connection = "jdbc:mysql://db-primary.internal.example.com:3306/mydb"

AWS Route53 offers private hosted zones that solve this elegantly. Here's how to implement it:

aws route53 create-hosted-zone \
  --name internal.example.com \
  --vpc VPCRegion=us-west-2,VPCId=vpc-1a2b3c4d \
  --caller-reference $(date +%s) \
  --hosted-zone-config Comment="Internal DNS",PrivateZone=true

With VPC DNS settings enabled (enableDnsHostnames and enableDnsSupport), EC2 instances automatically register:

  • Instance name: ip-10-0-1-25.ec2.internal
  • Reverse DNS: 25.1.0.10.in-addr.arpa

For custom naming (like db-primary.internal.example.com):

aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890 \
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "db-primary.internal.example.com",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [{ "Value": "10.0.1.25" }]
      }
    }]
  }'

For systems that can't use Route53 directly, consider a DNS forwarder:

# /etc/bind/named.conf.local
zone "internal.example.com" {
  type forward;
  forwarders { 10.0.0.2; }; # VPC DNS server
};

Verify your setup with dig/nslookup:

dig +short db-primary.internal.example.com
nslookup 10.0.1.25

Remember to configure your VPC DHCP options set to use the AmazonProvidedDNS server (169.254.169.253).


When working with non-RDS database servers or any inter-service communication within AWS VPC, relying on IP addresses creates several operational challenges:

# Bad practice - Hardcoded IP
database_url = "jdbc:postgresql://10.0.1.25:5432/mydb"

# Better practice - DNS name
database_url = "jdbc:postgresql://db-primary.internal.mydomain.com:5432/mydb"

AWS Route53 Private Hosted Zones provide a fully managed DNS service specifically for VPC environments. Unlike maintaining separate DNS servers or /etc/hosts files, this offers:

  • Automatic DNS resolution for EC2 instances using their hostnames
  • Seamless integration with AWS services (RDS, Elasticache, etc.)
  • High availability without managing servers
  • Tag-based DNS record management

Creating a Private Hosted Zone via AWS CLI:

aws route53 create-hosted-zone \
--name internal.mydomain.com \
--vpc VPCRegion=us-west-2,VPCId=vpc-1a2b3c4d \
--caller-reference $(date +%s) \
--hosted-zone-config Comment="Internal VPC DNS",PrivateZone=true

Automating DNS registration for EC2 instances:

#!/bin/bash
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
PRIVATE_IP=$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)
HOSTNAME=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=Name" --query 'Tags[0].Value' --output text)

aws route53 change-resource-record-sets --hosted-zone-id Z1234567890 \
--change-batch '{
  "Changes": [{
    "Action": "UPSERT",
    "ResourceRecordSet": {
      "Name": "'"$HOSTNAME"'.internal.mydomain.com",
      "Type": "A",
      "TTL": 300,
      "ResourceRecords": [{ "Value": "'"$PRIVATE_IP"'"}]
    }
  }]
}'

For complex environments, consider these approaches:

# Terraform configuration for Route53 Private Zone
resource "aws_route53_zone" "private" {
  name = "internal.mydomain.com"
  vpc {
    vpc_id = aws_vpc.main.id
  }
}

# DNS-based service discovery
resource "aws_service_discovery_private_dns_namespace" "example" {
  name        = "example.internal"
  description = "Service discovery namespace"
  vpc         = aws_vpc.main.id
}

When moving from legacy solutions (like MaraDNS or /etc/hosts):

  1. Start with a parallel DNS system during transition
  2. Update application configurations to use DNS names
  3. Monitor DNS resolution metrics in CloudWatch
  4. Gradually phase out old DNS infrastructure

Essential commands for DNS management:

# Test DNS resolution
dig +short db-primary.internal.mydomain.com

# Check Route53 resolver endpoints
aws ec2 describe-route53-resolver-endpoints

# View DNS query logs
aws route53resolver list-resolver-query-log-configs