Route 53: Custom Domains & DNS
Register domains, configure DNS, and connect your custom domain to AWS services. Add professional URLs to your deployed applications.
What you'll accomplish
Your application accessible at yourdomain.com with HTTPS
Prerequisites
- Application deployed on AWS (Lightsail, ECS, or App Runner)
- AWS account with CLI configured
What is Route 53?
Route 53 is AWS's DNS (Domain Name System) service. It:
- Registers new domain names
- Routes traffic to AWS resources
- Monitors health of your endpoints
- Manages DNS records
User types: myapp.com
↓
Route 53 DNS: "myapp.com → 52.23.156.178"
↓
Browser connects to your server
Note
Route 53 costs $0.50/month per hosted zone plus $12-50/year for domain registration (varies by TLD).
Understanding DNS Records
| Record Type | Purpose | Example |
|---|---|---|
| A | Points domain to IPv4 address | myapp.com → 52.23.156.178 |
| AAAA | Points domain to IPv6 address | myapp.com → 2001:db8::1 |
| CNAME | Points domain to another domain | www.myapp.com → myapp.com |
| ALIAS | AWS-specific, points to AWS resources | myapp.com → ALB DNS name |
| MX | Email server configuration | myapp.com → mail.myapp.com |
| TXT | Text records (verification, SPF) | "v=spf1 include:_spf.google.com" |
Note
ALIAS records are special to Route 53. Unlike CNAME, they work at the zone apex (naked domain like myapp.com, not www.myapp.com) and don't incur extra charges.
Option 1: Register a Domain with Route 53
Step 1: Search for Available Domains
- Go to Route 53 Console
- Click Registered domains → Register domains
- Search for your desired domain name
- Select a domain and click Proceed to checkout
Step 2: Complete Registration
- Enter registrant contact information
- Choose privacy protection (recommended, often free)
- Review and complete purchase
Step 3: Wait for Registration
Domain registration takes 15 minutes to several hours depending on the TLD.
bash# Check domain status via CLI aws route53domains get-domain-detail --domain-name yourdomain.com
Note
Route 53 automatically creates a hosted zone when you register a domain.
Option 2: Use a Domain from Another Registrar
If you have a domain from GoDaddy, Namecheap, Google Domains, etc.
Step 1: Create a Hosted Zone
bashaws route53 create-hosted-zone \ --name yourdomain.com \ --caller-reference $(date +%s)
Or in console:
- Go to Route 53 → Hosted zones
- Click Create hosted zone
- Enter your domain name
- Select Public hosted zone
- Click Create hosted zone
Step 2: Note the Name Servers
After creating the hosted zone, you'll see 4 NS records like:
ns-123.awsdns-12.com
ns-456.awsdns-34.net
ns-789.awsdns-56.org
ns-012.awsdns-78.co.uk
Step 3: Update Name Servers at Your Registrar
Go to your domain registrar and update the nameservers to the 4 AWS nameservers.
GoDaddy:
- My Products → DNS → Manage
- Change nameservers to custom
- Enter the 4 AWS nameservers
Namecheap:
- Domain List → Manage
- Nameservers → Custom DNS
- Enter the 4 AWS nameservers
Google Domains:
- DNS → Custom name servers
- Enter the 4 AWS nameservers
Note
DNS propagation takes 15 minutes to 48 hours. Be patient!
Connect Domain to Lightsail
For a Lightsail Instance
bash# Get your hosted zone ID ZONE_ID=$(aws route53 list-hosted-zones-by-name \ --dns-name yourdomain.com \ --query "HostedZones[0].Id" \ --output text | cut -d'/' -f3) # Create A record pointing to your Lightsail static IP aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "yourdomain.com", "Type": "A", "TTL": 300, "ResourceRecords": [{"Value": "YOUR_LIGHTSAIL_STATIC_IP"}] } }] }' # Add www subdomain aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "www.yourdomain.com", "Type": "CNAME", "TTL": 300, "ResourceRecords": [{"Value": "yourdomain.com"}] } }] }'
Via Console
- Go to Route 53 → Hosted zones → yourdomain.com
- Click Create record
- Leave record name empty (for naked domain)
- Record type: A
- Value: Your Lightsail static IP
- Click Create records
Repeat for www:
- Record name:
www - Record type: CNAME
- Value:
yourdomain.com
Connect Domain to ECS/Load Balancer
For ECS Fargate or any service behind an ALB:
Using ALIAS Record (Recommended)
bash# Get your ALB's hosted zone ID and DNS name ALB_ZONE_ID="YOUR_ALB_HOSTED_ZONE_ID" # e.g., Z35SXDOTRQ7X7K for us-east-1 ALB_DNS="myapp-alb-xxxxx.us-east-1.elb.amazonaws.com" aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "yourdomain.com", "Type": "A", "AliasTarget": { "HostedZoneId": "'$ALB_ZONE_ID'", "DNSName": "'$ALB_DNS'", "EvaluateTargetHealth": true } } }] }'
ALB Hosted Zone IDs by Region
| Region | Hosted Zone ID |
|---|---|
| us-east-1 | Z35SXDOTRQ7X7K |
| us-east-2 | Z3AADJGX6KTTL2 |
| us-west-1 | Z368ELLRRE2KJ0 |
| us-west-2 | Z1H1FL5HABSF5 |
| eu-west-1 | Z32O12XQLNTSW2 |
| eu-central-1 | Z215JYRZR1TBD5 |
| ap-southeast-1 | Z1LMS91P8CMLE5 |
| ap-northeast-1 | Z14GRHDCWA56QT |
Find more at AWS Documentation.
Connect Domain to App Runner
App Runner services have their own domain:
bash# Get App Runner service URL aws apprunner describe-service \ --service-arn YOUR_SERVICE_ARN \ --query "Service.ServiceUrl" # Create CNAME record aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "app.yourdomain.com", "Type": "CNAME", "TTL": 300, "ResourceRecords": [{"Value": "xxxxx.us-east-1.awsapprunner.com"}] } }] }'
Then configure the custom domain in App Runner:
bashaws apprunner associate-custom-domain \ --service-arn YOUR_SERVICE_ARN \ --domain-name app.yourdomain.com
Set Up SSL/HTTPS with ACM
Step 1: Request a Certificate
bash# Request certificate (must be in us-east-1 for CloudFront, any region for ALB) aws acm request-certificate \ --domain-name yourdomain.com \ --subject-alternative-names "*.yourdomain.com" \ --validation-method DNS \ --region us-east-1
Step 2: Validate with DNS
Get the validation records:
bashaws acm describe-certificate \ --certificate-arn YOUR_CERTIFICATE_ARN \ --query "Certificate.DomainValidationOptions"
Add the CNAME record to Route 53:
bash# The output gives you Name and Value - add them as a CNAME aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "_abc123.yourdomain.com", "Type": "CNAME", "TTL": 300, "ResourceRecords": [{"Value": "_xyz789.acm-validations.aws"}] } }] }'
Or use the automatic Route 53 integration in the console.
Step 3: Wait for Validation
bash# Check status aws acm describe-certificate \ --certificate-arn YOUR_CERTIFICATE_ARN \ --query "Certificate.Status" # Should change from PENDING_VALIDATION to ISSUED
Step 4: Attach to Load Balancer
For ECS/ALB:
bash# Create HTTPS listener aws elbv2 create-listener \ --load-balancer-arn YOUR_ALB_ARN \ --protocol HTTPS \ --port 443 \ --certificates CertificateArn=YOUR_CERTIFICATE_ARN \ --default-actions Type=forward,TargetGroupArn=YOUR_TARGET_GROUP_ARN
For Lightsail, use Let's Encrypt (covered in the Lightsail lessons).
Subdomains
Create records for subdomains:
bash# API subdomain aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "api.yourdomain.com", "Type": "A", "TTL": 300, "ResourceRecords": [{"Value": "YOUR_API_SERVER_IP"}] } }] }' # Staging environment aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "staging.yourdomain.com", "Type": "CNAME", "TTL": 300, "ResourceRecords": [{"Value": "staging-alb.us-east-1.elb.amazonaws.com"}] } }] }'
Configure Email (MX Records)
If you're using Google Workspace, Microsoft 365, or other email services:
Google Workspace
bashaws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "yourdomain.com", "Type": "MX", "TTL": 300, "ResourceRecords": [ {"Value": "1 aspmx.l.google.com"}, {"Value": "5 alt1.aspmx.l.google.com"}, {"Value": "5 alt2.aspmx.l.google.com"}, {"Value": "10 alt3.aspmx.l.google.com"}, {"Value": "10 alt4.aspmx.l.google.com"} ] } }] }'
Amazon SES
bashaws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "yourdomain.com", "Type": "MX", "TTL": 300, "ResourceRecords": [ {"Value": "10 inbound-smtp.us-east-1.amazonaws.com"} ] } }] }'
Test DNS Configuration
Using dig
Using Online Tools
- DNS Checker - Check propagation worldwide
- MX Toolbox - Comprehensive DNS lookup
- What's My DNS - Global propagation check
Troubleshooting
Domain Not Resolving
- Check nameservers are correct at registrar
- Wait for propagation (up to 48 hours)
- Verify hosted zone has the records
bashaws route53 list-resource-record-sets --hosted-zone-id $ZONE_ID
SSL Certificate Stuck in Pending
- Verify DNS validation record is correct
- Check the record exists:
bashdig _abc123.yourdomain.com CNAME
- Wait up to 30 minutes for validation
Wrong IP Address
bash# Update existing record aws route53 change-resource-record-sets \ --hosted-zone-id $ZONE_ID \ --change-batch '{ "Changes": [{ "Action": "UPSERT", "ResourceRecordSet": { "Name": "yourdomain.com", "Type": "A", "TTL": 300, "ResourceRecords": [{"Value": "NEW_IP_ADDRESS"}] } }] }'
HTTPS Not Working
- Verify certificate is in ISSUED status
- Check certificate is attached to load balancer
- Ensure HTTPS listener exists on port 443
- Verify security group allows port 443
Cost Summary
| Component | Cost |
|---|---|
| Hosted Zone | $0.50/month |
| DNS Queries (1M queries) | $0.40 |
| Domain Registration (.com) | $12-15/year |
| ACM Certificates | Free |
| Total | ~$1/month + domain |
Quick Reference: Common Record Patterns
# Naked domain to server
yourdomain.com A 52.23.156.178
# WWW to naked domain
www.yourdomain.com CNAME yourdomain.com
# Subdomain to load balancer
app.yourdomain.com ALIAS myapp-alb.us-east-1.elb.amazonaws.com
# API on different server
api.yourdomain.com A 52.23.156.179
# Staging environment
staging.yourdomain.com CNAME staging-alb.us-east-1.elb.amazonaws.com
What You've Accomplished
You now have a complete AWS deployment:
- AWS Account - Set up with billing alerts and IAM user
- AWS CLI - Configured on your local machine
- Application Deployed - Via Lightsail, ECS, or App Runner
- Custom Domain - Professional URL with Route 53
- SSL Certificate - HTTPS enabled with ACM
Note
Congratulations! Your application is now live at https://yourdomain.com. You've completed the end-to-end AWS deployment journey!
Next Steps
- Set up monitoring with CloudWatch
- Configure automated deployments with CodePipeline
- Add a CDN with CloudFront for global performance
- Implement blue-green deployments for zero-downtime updates