Home / AWS

AWSHow-toIntermediate

Route 53 Resolver query logging: see which DNS names your VPC workloads resolve

Turn on Route 53 Resolver query logging for a VPC, send it to CloudWatch Logs, and use Logs Insights to find NXDOMAIN storms, unexpected domains and which instance asked.

Deepak Bhardwaj4 min read

Applies toAmazon Route 53 Resolver, Amazon VPC, CloudWatch Logs, AWS CLI v2

Why turn it on

DNS problems in a VPC are hard to see. An application says "can't connect", a private endpoint resolves to a public IP, or a security team asks which instance looked up a suspicious domain. Without logs you are guessing.

Route 53 Resolver query logging records the DNS queries that workloads in a VPC send to the Amazon-provided resolver (the .2 address, or 169.254.169.253). Each record includes the name, type, response code, answers and the source IP and instance.

What it does not capture:

  • Queries sent straight to your own DNS servers (for example domain controllers) that never reach the Resolver.
  • Lookups answered from the instance's own OS cache.

Step 1: choose a destination

Destination Use it for
CloudWatch Logs Troubleshooting, Logs Insights queries, metric filters and alarms
S3 Cheap long-term retention, Athena queries
Kinesis Data Firehose Streaming to a SIEM

Create a log group with a retention period so the logs don't grow forever:

aws logs create-log-group --log-group-name /dns/vpc-prod
aws logs put-retention-policy --log-group-name /dns/vpc-prod --retention-in-days 30

Step 2: create the query logging configuration

LG_ARN=$(aws logs describe-log-groups --log-group-name-prefix /dns/vpc-prod \
  --query "logGroups[0].arn" --output text | sed 's/:\*$//')

aws route53resolver create-resolver-query-log-config \
  --name vpc-prod-dns \
  --destination-arn "$LG_ARN" \
  --creator-request-id "vpc-prod-dns-$(date +%s)"

Note the Id (starts with rqlc-) in the output.

Tip

When you set this up in the console, AWS adds the CloudWatch Logs resource policy that lets the service deliver logs. If you script it and no records arrive, check the log group's resource policy (aws logs describe-resource-policies) before anything else.

Step 3: associate VPCs

aws route53resolver associate-resolver-query-log-config \
  --resolver-query-log-config-id rqlc-0123456789abcdef \
  --resource-id vpc-0a1b2c3d4e5f

One configuration can be associated with many VPCs in the same Region. Check what is attached:

aws route53resolver list-resolver-query-log-config-associations \
  --query "ResolverQueryLogConfigAssociations[].{vpc:ResourceId,status:Status}" --output table

The status should go to ACTIVE. Logs appear within a few minutes of the next queries.

To share one configuration with other accounts, use AWS RAM; the owning account controls the destination.

Step 4: read the logs

A record looks like this (fields trimmed):

{
  "version": "1.100000",
  "account_id": "111122223333",
  "region": "eu-west-1",
  "vpc_id": "vpc-0a1b2c3d4e5f",
  "query_timestamp": "2026-10-27T09:14:02Z",
  "query_name": "mydb.privatelink.database.windows.net.",
  "query_type": "A",
  "rcode": "NXDOMAIN",
  "answers": [],
  "srcaddr": "10.20.1.45",
  "srcids": { "instance": "i-0abc1234def567890" }
}

Logs Insights queries to keep handy (run them against /dns/vpc-prod):

Names failing to resolve:

fields query_name, rcode
| filter rcode != "NOERROR"
| stats count(*) as failures by query_name, rcode
| sort failures desc
| limit 25

Who is asking for a given name:

fields @timestamp, srcaddr, srcids.instance, query_type, rcode
| filter query_name like /mydb\.privatelink/
| sort @timestamp desc
| limit 50

Top talkers (a script in a tight loop shows up here):

stats count(*) as queries by srcaddr, srcids.instance
| sort queries desc
| limit 20

What a private name actually resolved to (spot a public IP where you expected a private endpoint):

fields @timestamp, query_name, answers.0.Rdata as first_answer
| filter query_name like /blob\.core\.windows\.net|amazonaws\.com/
| sort @timestamp desc
| limit 50

Common findings

What you see Usual cause Fix
Steady NXDOMAIN for *.ec2.internal or a corporate suffix Wrong DNS search suffix on hosts Fix the DHCP options set or OS resolver config
NXDOMAIN for an on-premises zone Missing or unshared Resolver forwarding rule Add the rule and associate it with the VPC
Private endpoint name returns a public IP Private hosted zone not associated with this VPC Associate the zone with the VPC
SERVFAIL bursts Outbound endpoint can't reach on-prem DNS, or upstream DNS is down Check outbound endpoint security groups and on-prem DNS health
Thousands of identical queries per minute from one host Application not caching DNS Fix the app, or run a local caching resolver

Turning it off

aws route53resolver disassociate-resolver-query-log-config \
  --resolver-query-log-config-id rqlc-0123456789abcdef \
  --resource-id vpc-0a1b2c3d4e5f

Prevent it next time

  • Enable query logging in your VPC baseline (Terraform or CloudFormation) for every production VPC, not after an incident.
  • Add a metric filter on rcode = "SERVFAIL" and alarm on a sudden rise.
  • Keep 30 days in CloudWatch for troubleshooting and send a copy to S3 if security needs longer.
  • DNS logs are billed as log ingestion; check the current CloudWatch pricing for your volume before turning it on for chatty VPCs.