GeoDNS (Geographic DNS) returns different DNS responses based on the geographic location of the requesting client, enabling you to route users to the nearest server for lower latency. This guide covers implementing GeoDNS using PowerDNS with the GeoIP backend and BIND with response policy zones.
Use Cases
- CDN routing — direct users to the nearest edge server
- Regional compliance — serve EU users from EU data centers for GDPR
- Load distribution — spread traffic across geographic regions
- Disaster recovery — route away from failed regions
PowerDNS with GeoIP Backend
# Install PowerDNS with GeoIP
sudo apt install pdns-server pdns-backend-geoip
# Download GeoIP database (MaxMind GeoLite2)
# Register at maxmind.com for free GeoLite2 license key
wget "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=YOUR_KEY&suffix=tar.gz" -O GeoLite2-City.tar.gz
tar xzf GeoLite2-City.tar.gz
sudo cp GeoLite2-City_*/GeoLite2-City.mmdb /usr/share/GeoIP/
Configuration
# /etc/powerdns/pdns.conf
launch=geoip
geoip-database-files=/usr/share/GeoIP/GeoLite2-City.mmdb
geoip-zones-file=/etc/powerdns/geo-zones.yaml
# /etc/powerdns/geo-zones.yaml
domains:
- domain: example.com
ttl: 300
records:
# Default (fallback)
example.com:
- a:
content: 203.0.113.1
ttl: 300
# North America
example.com:
- a:
content: 198.51.100.1
ttl: 300
geo:
continent: NA
# Europe
example.com:
- a:
content: 192.0.2.1
ttl: 300
geo:
continent: EU
# Asia Pacific
example.com:
- a:
content: 198.51.100.50
ttl: 300
geo:
continent: AS
services:
example.com: [ "%co.%cn.geo.example.com", "%cn.geo.example.com", "default.geo.example.com" ]
BIND with GeoIP (view-based)
# /etc/bind/named.conf
acl "north-america" {
# ARIN IP ranges (simplified — use full GeoIP ACLs in production)
3.0.0.0/8; 4.0.0.0/8; 8.0.0.0/8;
};
acl "europe" {
2.0.0.0/8; 5.0.0.0/8; 31.0.0.0/8;
};
view "north-america" {
match-clients { north-america; };
zone "example.com" {
type master;
file "/etc/bind/zones/example.com.na";
};
};
view "europe" {
match-clients { europe; };
zone "example.com" {
type master;
file "/etc/bind/zones/example.com.eu";
};
};
view "default" {
match-clients { any; };
zone "example.com" {
type master;
file "/etc/bind/zones/example.com.default";
};
};
Cloud DNS GeoDNS
# AWS Route 53 Geolocation Routing
aws route53 change-resource-record-sets --hosted-zone-id Z1234 --change-batch '{
"Changes": [{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"SetIdentifier": "us-east",
"GeoLocation": { "ContinentCode": "NA" },
"TTL": 300,
"ResourceRecords": [{ "Value": "198.51.100.1" }]
}
}]
}'
# Cloudflare: Use load balancing with geo-steering
# Or use Workers for custom GeoDNS logic
Testing GeoDNS
# Test from different locations using EDNS Client Subnet
dig @your-geodns-server example.com A +subnet=198.51.100.0/24 # US IP
dig @your-geodns-server example.com A +subnet=2.0.0.0/8 # EU IP
# Use online tools
# https://www.whatsmydns.net — shows DNS responses from worldwide locations
# https://dnschecker.org — global DNS propagation check
Best Practices
- Always configure a default/fallback record for unmatched locations
- Use low TTL values (60-300 seconds) for GeoDNS records to enable quick failover
- Keep GeoIP databases updated monthly — IP allocations change frequently
- Monitor per-region server health and remove unhealthy servers from GeoDNS responses
- Test from multiple geographic locations before going live
- Consider latency-based routing instead of geography when possible — it is more accurate