Route53 Hosted Zone, Alias Record, and Health Check
Scenario
Your application is now running behind an Application Load Balancer with a long, autogenerated DNS name. You need to create a user-friendly domain name like app.example.com that points to the ALB using a Route53 Alias record. You also want a health check so Route53 can detect if the endpoint goes down.
- Create or reference an aws_route53_zone for your domain.
- Create an aws_route53_record of type A using an alias pointing to the ALB DNS name.
- Create an aws_route53_health_check that monitors the /health endpoint.
- Create a failover routing policy: primary record points to the ALB, secondary record points to an S3 static page as a maintenance fallback.
- Output the hosted zone ID and the record FQDN.
Additional Context
Alias records are Route53-specific — they resolve to the IP addresses of AWS resources (ALBs, CloudFront, S3, etc.) without incurring extra DNS query fees and without needing a TTL. Never use a CNAME for the zone apex (example.com) — only alias records work for zone apex because of the DNS CNAME restriction.
For the ALB alias record, you need the zone_id of the ALB, not your Route53 hosted zone. The ALB has its own fixed hosted zone ID per region — use the aws_lb data source or resource attribute zone_id.
versions.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}variables.tf
variable "aws_region" { type = string; default = "us-east-1" }
variable "domain_name" { type = string; description = "Root domain, e.g. example.com" }
variable "subdomain" { type = string; default = "app" }
variable "alb_dns_name" { type = string; description = "DNS name of your Application Load Balancer." }
variable "alb_zone_id" { type = string; description = "Hosted zone ID of the Application Load Balancer." }
variable "environment" { type = string; default = "prod" }main.tf
# main.tf
provider "aws" {
region = var.aws_region
}
# Look up the existing hosted zone — we assume the domain is already registered.
# Use data source if the zone exists; use aws_route53_zone resource if creating new.
data "aws_route53_zone" "main" {
name = var.domain_name
private_zone = false
}
# Health check — monitors the app endpoint every 30 seconds.
# Route53 uses this to determine if the primary record is healthy.
resource "aws_route53_health_check" "app" {
fqdn = "${var.subdomain}.${var.domain_name}"
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = 3 # declare unhealthy after 3 consecutive failures
request_interval = 30 # check every 30 seconds
tags = { Name = "app-health-check", Environment = var.environment }
}
# Primary A record — alias to the ALB.
# When health check fails, Route53 stops routing to this record.
resource "aws_route53_record" "app_primary" {
zone_id = data.aws_route53_zone.main.zone_id
name = "${var.subdomain}.${var.domain_name}"
type = "A"
# Failover routing: this record is the PRIMARY.
failover_routing_policy {
type = "PRIMARY"
}
set_identifier = "primary"
health_check_id = aws_route53_health_check.app.id
# Alias to the ALB — no TTL needed for alias records.
alias {
name = var.alb_dns_name
zone_id = var.alb_zone_id
evaluate_target_health = true
}
}
# Secondary (failover) record — points to a static "maintenance" page on S3.
# Only active when the primary health check fails.
resource "aws_route53_record" "app_secondary" {
zone_id = data.aws_route53_zone.main.zone_id
name = "${var.subdomain}.${var.domain_name}"
type = "CNAME"
ttl = 60
failover_routing_policy {
type = "SECONDARY"
}
set_identifier = "secondary"
# Replace with your S3 static website endpoint if desired.
records = ["maintenance.${var.domain_name}"]
}outputs.tf
output "hosted_zone_id" { value = data.aws_route53_zone.main.zone_id }
output "app_fqdn" { value = "${var.subdomain}.${var.domain_name}" }
output "health_check_id"{ value = aws_route53_health_check.app.id }Workflow Commands
terraform init
terraform apply \
-var="domain_name=example.com" \
-var="alb_dns_name=web-alb-prod-xxx.us-east-1.elb.amazonaws.com" \
-var="alb_zone_id=Z35SXDOTRQ7X7K"
# Test DNS resolution (may take a minute to propagate)
dig app.example.com
# Check health check status
aws route53 get-health-check-status \
--health-check-id $(terraform output -raw health_check_id)
terraform destroy -var="domain_name=example.com" -var="alb_dns_name=..." -var="alb_zone_id=..."Apply complete! Resources: 3 added, 0 changed, 0 destroyed. Outputs: app_fqdn = "app.example.com" health_check_id = "12345678-abcd-1234-abcd-123456789012" hosted_zone_id = "Z0123456789ABCDEFGHIJ"

