Understanding Route 53 Monitoring
Amazon Route 53 is a highly available and scalable DNS web service, but like any critical infrastructure component, it requires continuous observability. Monitoring Route 53 means tracking the health and performance of your DNS resolution, health checks, and routing policies through CloudWatch metrics, alarms, and dashboards. This gives you visibility into query volumes, failure rates, latency, and the operational state of your endpoints.
Route 53 monitoring operates across three interconnected layers: DNS query metrics for public and private hosted zones, health check metrics that track endpoint reachability, and routing control metrics when using Route 53 Application Recovery Controller. Each layer publishes data to CloudWatch, where you can build alarms, visualize trends, and trigger automated responses.
Why Route 53 Monitoring Matters
Without proper monitoring, DNS failures can silently cascade into application outages. A misconfigured health check might stop sending traffic to a healthy endpoint, or a sudden spike in DNS queries could indicate a DDoS attack. Monitoring helps you:
- Detect anomalies early — unusual query patterns or rising failure rates signal problems before users are impacted
- Validate failover behavior — confirm that Route 53 actually shifts traffic when an endpoint becomes unhealthy
- Optimize routing policies — latency-based routing depends on accurate CloudWatch metric data
- Meet compliance requirements — prove DNS infrastructure availability and response times to auditors
- Troubleshoot faster — correlate DNS metrics with application logs during incident response
Key Route 53 Metrics in CloudWatch
Route 53 publishes metrics to CloudWatch in the AWS/Route53 namespace. These metrics fall into distinct categories, and understanding each one is essential before building alarms or dashboards.
DNS Query Metrics for Hosted Zones
For public hosted zones, Route 53 automatically publishes DNS query metrics aggregated across all edge locations. These are available in US East (N. Virginia) regardless of where your hosted zone is defined:
- DNSQueries — total number of DNS queries answered by Route 53 for a hosted zone. Useful for spotting traffic trends and potential volumetric attacks
- NoAnswer — queries that received no answer (e.g., NXDOMAIN for non-existent records). A spike here may indicate misconfigured clients or expired records
- DNSQueriesByType — breaks down queries by record type (A, AAAA, CNAME, MX, etc.) across edge locations
- DNSQueriesByTypeAndEdge — query counts by record type and specific edge location for granular geographic analysis
Health Check Metrics
Every Route 53 health check emits metrics to CloudWatch under the namespace AWS/Route53 with the dimension HealthCheckId. These are the most actionable metrics for alarming:
- HealthCheckStatus — binary indicator: 1 when the health check passes, 0 when it fails. The foundation for failover routing decisions
- HealthCheckPercentageHealthy — aggregated percentage across all health checkers (typically 0%, 50%, or 100% depending on the failure threshold configuration)
- TimeToFirstByte — milliseconds from the health checker's TCP connection to the first byte of the response. Measures raw endpoint latency
- TCPConnectionTime — time to establish a TCP connection, useful for diagnosing network-level issues versus application slowness
- SSLHandshakeTime — time to complete the SSL/TLS handshake for HTTPS health checks
- ChildHealthCheckHealthyCount — for calculated health checks, the number of child checks currently healthy
Latency-Based Routing Metrics
When you use latency-based routing, Route 53 relies on aggregated latency data from CloudWatch. The relevant metric is Latency in the AWS/Route53 namespace, though this data is typically consumed internally rather than alarmed against directly.
Setting Up Route 53 Health Checks
A health check defines how Route 53 verifies that an endpoint is reachable and responsive. You can create HTTP, HTTPS, or TCP health checks, and optionally combine them into calculated health checks for complex scenarios.
Creating a Basic HTTP Health Check via AWS CLI
The following command creates an HTTP health check that monitors api.example.com on port 443 using HTTPS, with a 30-second interval and a failure threshold of 3 consecutive failures:
aws route53 create-health-check \
--caller-reference "api-health-check-$(date +%s)" \
--health-check-config '{
"IPAddress": "203.0.113.10",
"Port": 443,
"Type": "HTTPS",
"ResourcePath": "/health",
"FullyQualifiedDomainName": "api.example.com",
"RequestInterval": 30,
"FailureThreshold": 3,
"MeasureLatency": true,
"EnableSNI": true,
"Regions": ["us-east-1", "us-west-2", "eu-west-1"]
}'
The MeasureLatency flag ensures the TimeToFirstByte and TCPConnectionTime metrics populate in CloudWatch. The Regions array distributes health checkers across multiple geographic locations, reducing false positives from regional network issues.
Calculated Health Checks
A calculated health check aggregates multiple child health checks using AND/OR logic. This is critical for multi-region deployments where you want to fail over only when all primary regions are down:
aws route53 create-health-check \
--caller-reference "calculated-primary-$(date +%s)" \
--health-check-config '{
"Type": "CALCULATED",
"HealthThreshold": 2,
"ChildHealthChecks": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a7-8901-bcde-f12345678901",
"c3d4e5f6-a7b8-9012-cdef-123456789012"
]
}'
With HealthThreshold set to 2, at least two of the three child health checks must pass for the overall check to be healthy. This prevents a single regional health checker glitch from triggering an unnecessary failover.
Building CloudWatch Alarms for Route 53
CloudWatch alarms translate Route 53 metrics into actionable notifications. The most common alarms focus on health check failures, but you should also consider DNS query anomalies and latency thresholds.
Health Check Failure Alarm
This alarm triggers when a health check reports a status of 0 for two consecutive evaluation periods, indicating the endpoint is down:
aws cloudwatch put-metric-alarm \
--alarm-name "primary-endpoint-health-failure" \
--alarm-description "Triggers when primary endpoint health check fails" \
--namespace "AWS/Route53" \
--metric-name "HealthCheckStatus" \
--dimensions "Name=HealthCheckId,Value=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
--statistic "Minimum" \
--period 60 \
--evaluation-periods 2 \
--threshold 1.0 \
--comparison-operator "LessThanThreshold" \
--treat-missing-data "breaching" \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:route53-alerts" \
--ok-actions "arn:aws:sns:us-east-1:123456789012:route53-alerts"
The treat-missing-data parameter set to breaching is critical here. If CloudWatch stops receiving data from the health checker (which itself indicates a problem), the alarm enters the ALARM state rather than staying green silently.
DNS Query Spike Alarm
Detect abnormal query volumes that might indicate a DDoS attack or misbehaving client:
aws cloudwatch put-metric-alarm \
--alarm-name "dns-query-spike" \
--alarm-description "Triggers when DNS queries exceed 10x baseline" \
--namespace "AWS/Route53" \
--metric-name "DNSQueries" \
--dimensions "Name=HostedZoneId,Value=Z1234567890ABC" \
--statistic "Sum" \
--period 300 \
--evaluation-periods 3 \
--threshold 100000 \
--comparison-operator "GreaterThanThreshold" \
--treat-missing-data "notBreaching" \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:dns-security-alerts"
Latency Threshold Alarm for Health Checkers
Alert when endpoint latency exceeds SLA thresholds, even if the health check hasn't failed yet:
aws cloudwatch put-metric-alarm \
--alarm-name "api-latency-high" \
--alarm-description "API response time exceeds 2 seconds" \
--namespace "AWS/Route53" \
--metric-name "TimeToFirstByte" \
--dimensions "Name=HealthCheckId,Value=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
--statistic "Average" \
--period 120 \
--evaluation-periods 2 \
--threshold 2000 \
--comparison-operator "GreaterThanThreshold" \
--unit "Milliseconds" \
--treat-missing-data "ignore" \
--alarm-actions "arn:aws:sns:us-east-1:123456789012:performance-alerts"
Creating CloudWatch Dashboards for Route 53
Dashboards provide a unified view of DNS health across all your hosted zones, health checks, and routing policies. You can create dashboards programmatically using the CloudWatch API, AWS CLI, or Infrastructure as Code tools.
Dashboard JSON Structure Overview
A CloudWatch dashboard is defined as a JSON string containing an array of widgets. Each widget specifies its type (line, metric, text, etc.), position, size, and the metrics to display. Here's the foundational structure:
{
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "DNSQueries", { "stat": "Sum" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 300,
"title": "DNS Queries - Public Hosted Zone"
}
}
]
}
Complete Route 53 Monitoring Dashboard
The following CLI command creates a comprehensive dashboard with multiple widgets covering DNS queries, health check status, latency, and query type breakdown:
aws cloudwatch put-dashboard \
--dashboard-name "Route53-Monitoring" \
--dashboard-body '{
"widgets": [
{
"type": "text",
"x": 0,
"y": 0,
"width": 24,
"height": 2,
"properties": {
"markdown": "# Route 53 Global Dashboard\n\n**Public Hosted Zone:** Z1234567890ABC | **Health Check:** primary-endpoint | **Region:** us-east-1"
}
},
{
"type": "metric",
"x": 0,
"y": 2,
"width": 8,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "DNSQueries", { "HostedZoneId": "Z1234567890ABC", "stat": "Sum", "label": "Total Queries" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 300,
"title": "DNS Query Volume"
}
},
{
"type": "metric",
"x": 8,
"y": 2,
"width": 8,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "NoAnswer", { "HostedZoneId": "Z1234567890ABC", "stat": "Sum", "label": "NXDOMAIN/NoAnswer" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 300,
"title": "Unanswered Queries"
}
},
{
"type": "metric",
"x": 16,
"y": 2,
"width": 8,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "DNSQueriesByType", { "HostedZoneId": "Z1234567890ABC", "stat": "Sum", "label": "A" } ],
[ ".", "DNSQueriesByType", { "HostedZoneId": "Z1234567890ABC", "DnsQueryType": "AAAA", "stat": "Sum", "label": "AAAA" } ],
[ ".", "DNSQueriesByType", { "HostedZoneId": "Z1234567890ABC", "DnsQueryType": "CNAME", "stat": "Sum", "label": "CNAME" } ],
[ ".", "DNSQueriesByType", { "HostedZoneId": "Z1234567890ABC", "DnsQueryType": "MX", "stat": "Sum", "label": "MX" } ]
],
"view": "timeSeries",
"stacked": true,
"region": "us-east-1",
"period": 300,
"title": "Queries by Record Type"
}
},
{
"type": "metric",
"x": 0,
"y": 8,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "HealthCheckStatus", { "HealthCheckId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "stat": "Minimum", "label": "Health Status (1=Healthy, 0=Unhealthy)" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 60,
"title": "Health Check Status",
"yAxis": {
"left": {
"min": 0,
"max": 1
}
}
}
},
{
"type": "metric",
"x": 12,
"y": 8,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "TimeToFirstByte", { "HealthCheckId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "stat": "p90", "label": "TTFB p90" } ],
[ ".", "TimeToFirstByte", { "HealthCheckId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "stat": "Average", "label": "TTFB Avg" } ],
[ ".", "TCPConnectionTime", { "HealthCheckId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "stat": "Average", "label": "TCP Connect" } ],
[ ".", "SSLHandshakeTime", { "HealthCheckId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "stat": "Average", "label": "SSL Handshake" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 120,
"title": "Health Check Latency Breakdown (ms)"
}
},
{
"type": "metric",
"x": 0,
"y": 14,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "HealthCheckPercentageHealthy", { "HealthCheckId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "stat": "Average", "label": "% Healthy Checkers" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 60,
"title": "Percentage of Healthy Health Checkers",
"yAxis": {
"left": {
"min": 0,
"max": 100
}
}
}
},
{
"type": "metric",
"x": 12,
"y": 14,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "ChildHealthCheckHealthyCount", { "HealthCheckId": "calculated-check-id", "stat": "Average", "label": "Healthy Children" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 60,
"title": "Calculated Health Check - Healthy Children"
}
}
]
}'
Using AWS CLI to Retrieve Current Dashboard
To iterate on an existing dashboard, retrieve its current definition, modify it, and push an update:
# Get current dashboard definition
aws cloudwatch get-dashboard \
--dashboard-name "Route53-Monitoring" \
--query 'DashboardBody' \
--output text > dashboard-current.json
# Edit dashboard-current.json with your changes
# Push updated dashboard
aws cloudwatch put-dashboard \
--dashboard-name "Route53-Monitoring" \
--dashboard-body file://dashboard-current.json
Infrastructure as Code for Route 53 Monitoring
CloudFormation Template Example
Defining monitoring resources in CloudFormation ensures consistency across environments and enables reproducible deployments:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Route 53 Monitoring Stack - Health Checks and Alarms'
Parameters:
EndpointIP:
Type: String
Description: IP address of the monitored endpoint
EndpointFQDN:
Type: String
Description: Fully qualified domain name of the endpoint
SnsTopicArn:
Type: String
Description: ARN of SNS topic for alarm notifications
Resources:
PrimaryHealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
HealthCheckConfig:
IPAddress: !Ref EndpointIP
Port: 443
Type: HTTPS
ResourcePath: /health
FullyQualifiedDomainName: !Ref EndpointFQDN
RequestInterval: 30
FailureThreshold: 3
MeasureLatency: true
EnableSNI: true
Regions:
- us-east-1
- us-west-2
- eu-west-1
HealthCheckTags:
- Key: Environment
Value: Production
- Key: Service
Value: API
HealthCheckAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub "${EndpointFQDN}-health-failure"
AlarmDescription: !Sub "Health check failure for ${EndpointFQDN}"
Namespace: AWS/Route53
MetricName: HealthCheckStatus
Dimensions:
- Name: HealthCheckId
Value: !Ref PrimaryHealthCheck
Statistic: Minimum
Period: 60
EvaluationPeriods: 2
Threshold: 1.0
ComparisonOperator: LessThanThreshold
TreatMissingData: breaching
AlarmActions:
- !Ref SnsTopicArn
OKActions:
- !Ref SnsTopicArn
LatencyAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub "${EndpointFQDN}-high-latency"
AlarmDescription: !Sub "TTFB exceeds 2000ms for ${EndpointFQDN}"
Namespace: AWS/Route53
MetricName: TimeToFirstByte
Dimensions:
- Name: HealthCheckId
Value: !Ref PrimaryHealthCheck
Statistic: Average
Period: 120
EvaluationPeriods: 2
Threshold: 2000
Unit: Milliseconds
ComparisonOperator: GreaterThanThreshold
TreatMissingData: ignore
AlarmActions:
- !Ref SnsTopicArn
Route53Dashboard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardName: !Sub "${EndpointFQDN}-route53-monitoring"
DashboardBody: !Sub |
{
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "HealthCheckStatus", { "HealthCheckId": "${PrimaryHealthCheck}", "stat": "Minimum", "label": "Health Status" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 60,
"title": "Health Check Status",
"yAxis": { "left": { "min": 0, "max": 1 } }
}
},
{
"type": "metric",
"x": 12,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"metrics": [
[ "AWS/Route53", "TimeToFirstByte", { "HealthCheckId": "${PrimaryHealthCheck}", "stat": "p90", "label": "TTFB p90" } ],
[ ".", "TimeToFirstByte", { "HealthCheckId": "${PrimaryHealthCheck}", "stat": "Average", "label": "TTFB Avg" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"period": 120,
"title": "Latency Metrics (ms)"
}
}
]
}
CDK Example (TypeScript)
For teams using the AWS CDK, here's a reusable construct that encapsulates health check creation, alarming, and dashboard setup:
import * as cdk from 'aws-cdk-lib';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as constructs from 'constructs';
interface Route53MonitoringProps {
endpointIp: string;
endpointFqdn: string;
resourcePath: string;
alarmTopic: sns.ITopic;
failureThreshold?: number;
requestInterval?: number;
}
export class Route53MonitoringConstruct extends constructs.Construct {
public readonly healthCheck: route53.CfnHealthCheck;
public readonly healthAlarm: cloudwatch.Alarm;
public readonly latencyAlarm: cloudwatch.Alarm;
public readonly dashboard: cloudwatch.Dashboard;
constructor(scope: constructs.Construct, id: string, props: Route53MonitoringProps) {
super(scope, id);
const failureThreshold = props.failureThreshold ?? 3;
const requestInterval = props.requestInterval ?? 30;
// Create the health check
this.healthCheck = new route53.CfnHealthCheck(this, 'HealthCheck', {
healthCheckConfig: {
ipAddress: props.endpointIp,
port: 443,
type: 'HTTPS',
resourcePath: props.resourcePath,
fullyQualifiedDomainName: props.endpointFqdn,
requestInterval: requestInterval,
failureThreshold: failureThreshold,
measureLatency: true,
enableSni: true,
regions: ['us-east-1', 'us-west-2', 'eu-west-1'],
},
});
// Health check status alarm
this.healthAlarm = new cloudwatch.Alarm(this, 'HealthAlarm', {
alarmName: `${props.endpointFqdn}-health-failure`,
alarmDescription: `Health check failure for ${props.endpointFqdn}`,
metric: new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'HealthCheckStatus',
dimensionsMap: {
HealthCheckId: this.healthCheck.attrHealthCheckId,
},
statistic: 'Minimum',
period: cdk.Duration.seconds(60),
}),
threshold: 1,
evaluationPeriods: 2,
comparisonOperator: cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
treatMissingData: cloudwatch.TreatMissingData.BREACHING,
});
this.healthAlarm.addAlarmAction(new cloudwatch.SnsAction(props.alarmTopic));
this.healthAlarm.addOkAction(new cloudwatch.SnsAction(props.alarmTopic));
// Latency alarm
this.latencyAlarm = new cloudwatch.Alarm(this, 'LatencyAlarm', {
alarmName: `${props.endpointFqdn}-high-latency`,
alarmDescription: `TTFB exceeds 2000ms for ${props.endpointFqdn}`,
metric: new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'TimeToFirstByte',
dimensionsMap: {
HealthCheckId: this.healthCheck.attrHealthCheckId,
},
statistic: 'Average',
period: cdk.Duration.seconds(120),
}),
threshold: 2000,
evaluationPeriods: 2,
comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
unit: cloudwatch.Unit.MILLISECONDS,
treatMissingData: cloudwatch.TreatMissingData.IGNORE,
});
this.latencyAlarm.addAlarmAction(new cloudwatch.SnsAction(props.alarmTopic));
// Monitoring dashboard
this.dashboard = new cloudwatch.Dashboard(this, 'Dashboard', {
dashboardName: `${props.endpointFqdn.replace(/\./g, '-')}-route53-monitoring`,
widgets: [
new cloudwatch.SingleValueWidget({
title: 'Current Health Status',
metrics: [
new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'HealthCheckStatus',
dimensionsMap: { HealthCheckId: this.healthCheck.attrHealthCheckId },
statistic: 'Minimum',
period: cdk.Duration.seconds(60),
}),
],
height: 6,
width: 6,
}),
new cloudwatch.GraphWidget({
title: 'Health Check Latency (ms)',
left: [
new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'TimeToFirstByte',
dimensionsMap: { HealthCheckId: this.healthCheck.attrHealthCheckId },
statistic: 'p90',
label: 'TTFB p90',
period: cdk.Duration.seconds(120),
}),
new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'TCPConnectionTime',
dimensionsMap: { HealthCheckId: this.healthCheck.attrHealthCheckId },
statistic: 'Average',
label: 'TCP Connect',
period: cdk.Duration.seconds(120),
}),
new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'SSLHandshakeTime',
dimensionsMap: { HealthCheckId: this.healthCheck.attrHealthCheckId },
statistic: 'Average',
label: 'SSL Handshake',
period: cdk.Duration.seconds(120),
}),
],
height: 6,
width: 12,
}),
new cloudwatch.GraphWidget({
title: 'DNS Query Volume (5-min sum)',
left: [
new cloudwatch.Metric({
namespace: 'AWS/Route53',
metricName: 'DNSQueries',
dimensionsMap: { HostedZoneId: 'Z1234567890ABC' },
statistic: 'Sum',
label: 'Total Queries',
period: cdk.Duration.seconds(300),
}),
],
height: 6,
width: 6,
}),
],
});
// Output useful information
new cdk.CfnOutput(this, 'HealthCheckId', {
value: this.healthCheck.attrHealthCheckId,
description: 'The ID of the Route 53 health check',
});
}
}
Terraform Example
For teams using Terraform, here's a complete monitoring module covering health checks, alarms, and a dashboard:
# variables.tf
variable "endpoint_ip" {
type = string
description = "IP address of the monitored endpoint"
}
variable "endpoint_fqdn" {
type = string
description = "FQDN of the monitored endpoint"
}
variable "alarm_sns_topic_arn" {
type = string
description = "ARN of SNS topic for alarm notifications"
}
variable "hosted_zone_id" {
type = string
description = "Route 53 hosted zone ID for DNS query monitoring"
}
# main.tf
resource "aws_route53_health_check" "primary" {
fqdn = var.endpoint_fqdn
ip_address = var.endpoint_ip
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = 3
request_interval = 30
measure_latency = true
enable_sni = true
regions = ["us-east-1", "us-west-2", "eu-west-1"]
tags = {
Name = "primary-endpoint-health-check"
Environment = "production"
}
}
resource "aws_cloudwatch_metric_alarm" "health_check_failure" {
alarm_name = "${var.endpoint_fqdn}-health-failure"
alarm_description = "Health check failure for ${var.endpoint_fqdn}"
namespace = "AWS/Route53"
metric_name = "HealthCheckStatus"
statistic = "Minimum"
period = 60
evaluation_periods = 2
threshold = 1.0
comparison_operator = "LessThanThreshold"
treat_missing_data = "breaching"
dimensions = {
HealthCheckId = aws_route53_health_check.primary.id
}
alarm_actions = [var.alarm_sns_topic_arn]
ok_actions = [var.alarm_sns_topic_arn]
}
resource "aws_cloudwatch_metric_alarm" "high_latency" {
alarm_name = "${var.endpoint_fqdn}-high-latency"
alarm_description = "TTFB exceeds 2000ms for ${var.endpoint_fqdn}"
namespace = "AWS/Route53"
metric_name = "TimeToFirstByte"
statistic = "Average"
period = 120
evaluation_periods = 2
threshold = 2000
unit = "Milliseconds"
comparison_operator = "GreaterThanThreshold"
treat_missing_data = "ignore"
dimensions = {
HealthCheckId = aws_route53_health_check.primary.id
}
alarm_actions = [var.alarm_sns_topic_arn]
}
resource "aws_cloudwatch_dashboard" "route53_monitoring" {
dashboard_name = "${replace(var.endpoint_fqdn, ".", "-")}-route53-monitoring"
dashboard_body = jsonencode({
widgets = [
{
type = "metric"
x = 0
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/Route53", "HealthCheckStatus", { HealthCheckId = aws_route53_health_check.primary.id, stat = "Minimum", label = "Health Status" }]
]
view = "timeSeries"
stacked = false
region = "us-east-1"
period = 60
title = "Health Check Status"
yAxis = { left = { min = 0, max = 1 } }
}
},
{
type = "metric"
x = 12
y = 0
width = 12
height = 6
properties = {
metrics = [
["AWS/Route53", "TimeToFirstByte", { HealthCheckId = aws_route53_health_check.primary.id, stat = "p90", label = "TTFB p90" }],
[".", "TimeToFirstByte", { HealthCheckId = aws_route53_health_check.primary.id, stat = "Average", label = "TTFB Avg" }],
[".", "TCPConnectionTime", { HealthCheckId = aws_route53_health_check.primary.id, stat = "Average", label = "TCP Connect" }]
]