← Back to posts
comparison guide self-hosted · · 13 min read

PowerDNS vs BIND9 vs CoreDNS: Self-Hosted GeoDNS Routing Guide 2026

Complete guide to self-hosted GeoDNS routing with PowerDNS, BIND9, and CoreDNS. Route users to the nearest server based on geographic location for lower latency and better performance.

OS
Editorial Team

When you run services across multiple data centers or cloud regions, getting users to the nearest endpoint matters. Every extra hop adds latency, and a user in Tokyo hitting your US East server can easily see 150-200ms of unnecessary round-trip time. Geographic DNS — or GeoDNS — solves this at the DNS layer by returning different IP addresses based on where the query originates.

While managed services like Cloudflare Load Balancing or AWS Route 53 Latency Routing offer this out of the box, running your own GeoDNS gives you full control, zero per-query costs, and no vendor lock-in. This guide covers three open-source approaches to self-hosted GeoDNS: PowerDNS with its GeoIP backend, BIND9 with views, and CoreDNS with the geoip plugin.

Why Self-Host GeoDNS?

GeoDNS routing is a foundational technique for any globally distributed infrastructure. Whether you’re serving a web application from multiple regions, running a CDN edge network, or distributing API traffic across continents, DNS-level geographic routing delivers tangible benefits:

  • Reduced latency: Users get routed to the physically closest server, cutting round-trip time by 50-80% for international traffic.
  • Compliance and data residency: Route European users to EU-based servers to satisfy GDPR requirements, or keep traffic within specific jurisdictions.
  • Cost control: Managed GeoDNS providers charge per million queries. At scale, self-hosting saves thousands of dollars per year.
  • Failover integration: Combine geographic routing with health checks so traffic automatically shifts when a region goes down.
  • No vendor lock-in: Your DNS configuration lives in your own infrastructure, not a cloud provider’s proprietary format.

If you’re already running your own self-hosted DNS servers, adding GeoDNS capabilities is a natural next step. And for broader load-balancing needs, you can pair GeoDNS with DNS-level load balancing tools or reverse proxy solutions.

How GeoDNS Works

GeoDNS operates by examining the source IP address of each DNS query and mapping it to a geographic region using a GeoIP database. The DNS server then returns a different A or AAAA record depending on which region the query came from.

The workflow looks like this:

  1. A user’s resolver sends a DNS query for app.example.com
  2. Your authoritative DNS server extracts the resolver’s IP address
  3. A GeoIP database (MaxMind, DB-IP, or IP2Location) maps that IP to a country, continent, or city
  4. The DNS server selects the matching record set for that region
  5. The resolver receives an IP address pointing to the nearest regional endpoint

The accuracy of GeoDNS depends on the quality of your GeoIP database. Country-level accuracy is typically 95-99%, while city-level accuracy ranges from 50-80%. For most GeoDNS use cases, country or continent-level routing is sufficient and more reliable.

PowerDNS Authoritative with GeoIP Backend

PowerDNS is one of the most flexible authoritative DNS servers available, with a modular backend architecture that lets you store zone data in various sources — SQL databases, zone files, and GeoIP databases. The GeoIP backend, geoip, is specifically designed for geographic DNS routing.

Key stats (as of April 2026): 4,345 GitHub stars, last updated April 17, 2026. Written in C++.

How PowerDNS GeoIP Works

The PowerDNS GeoIP backend reads a YAML zone file that maps IP ranges (via GeoIP lookups) to specific record sets. You define regions — typically by country code or continent code — and assign different A records to each region.

GeoIP Zone File Format

PowerDNS uses a custom YAML format for GeoIP zones. Here’s a complete example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# /etc/pdns/zones/app-example-com.yml
$TTL 3600
@   SOA  ns1.example.com. admin.example.com. 2026041901 3600 900 604800 3600
    NS   ns1.example.com.

$geoip /usr/share/GeoIP/GeoLite2-Country.mmdb

$default
@   IN  A   203.0.113.10    # Default/fallback IP (US)
www IN  A   203.0.113.10

$country US
@   IN  A   203.0.113.10    # US East
www IN  A   203.0.113.10

$country GB
@   IN  A   198.51.100.10   # London
www IN  A   198.51.100.10

$country DE
@   IN  A   198.51.100.20   # Frankfurt
www IN  A   198.51.100.20

$country JP
@   IN  A   192.0.2.10      # Tokyo
www IN  A   192.0.2.10

$continent AS
@   IN  A   192.0.2.20      # Asia fallback (Singapore)
www IN  A   192.0.2.20

PowerDNS Configuration

Add the GeoIP backend to your pdns.conf:

1
2
3
4
5
6
7
8
9
# /etc/pdns/pdns.conf
launch=geoip
geoip-zones-file=/etc/pdns/zones/app-example-com.yml
geoip-database-files=/usr/share/GeoIP/GeoLite2-Country.mmdb
api=yes
api-key=your-api-key
webserver=yes
webserver-address=0.0.0.0
webserver-port=8081

docker Compose Setup

PowerDNS provides an official docker-compose configuration. Here’s a production-ready setup combining the authoritative server with a backend database:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# docker-compose.yml
version: '3.8'

services:
  pdns-auth:
    image: powerdns/pdns-auth-49:latest
    environment:
      - PDNS_AUTH_API_KEY=your-secret-api-key
      - PDNS_AUTH_PRIMARY=sqlite:///data/pdns.sqlite
    volumes:
      - ./zones:/etc/pdns/zones:ro
      - ./geoip:/usr/share/GeoIP:ro
      - pdns-data:/data
    ports:
      - "53:53"
      - "53:53/udp"
      - "8081:8081"
    restart: unless-stopped
    networks:
      dns-net:
        ipv4_address: 172.20.0.10

  pdns-admin:
    image: powerdns/pdns-admin:latest
    environment:
      - PDNS_API_KEY=your-secret-api-key
      - PDNS_API_URL=http://pdns-auth:8081
    ports:
      - "8082:80"
    depends_on:
      - pdns-auth
    restart: unless-stopped
    networks:
      - dns-net

volumes:
  pdns-data:

networks:
  dns-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

Download the GeoIP database from MaxMind (free GeoLite2 version available) and place it in the geoip/ directory. PowerDNS will automatically perform GeoIP lookups for each query and return the region-appropriate record.

BIND9 with Views

BIND9 is the oldest and most widely deployed DNS server. Its “views” feature provides a built-in mechanism for geographic DNS routing without requiring any additional backends or plugins.

Key stats (as of April 2026): 737 GitHub stars, last updated August 2025. Written in C. Maintained by ISC (Internet Systems Consortium).

How BIND9 Views Work

BIND9 views partition the DNS namespace into different “views” based on the source IP of the query. Each view can have its own zone data, meaning you can serve different A records to different geographic regions. You define ACLs (Access Control Lists) for IP ranges corresponding to regions, then assign each ACL to a view.

BIND9 GeoDNS Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// /etc/bind/named.conf.options
options {
    directory "/var/cache/bind";
    recursion no;
    allow-query { any; };
};

// GeoIP-based ACLs
// Note: these are approximate ranges; use a GeoIP-to-CIDR mapping tool
acl "region-us" {
    203.0.113.0/24;    // Example US range
    // Add more CIDR blocks for US IPs
};

acl "region-europe" {
    198.51.100.0/24;   // Example EU range
    // Add more CIDR blocks for EU IPs
};

acl "region-asia" {
    192.0.2.0/24;      // Example Asia range
    // Add more CIDR blocks for Asia IPs
};

acl "region-default" {
    any;
};

// Views for each region
view "us-view" {
    match-clients { region-us; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db-example-us";
    };
};

view "europe-view" {
    match-clients { region-europe; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db-example-eu";
    };
};

view "asia-view" {
    match-clients { region-asia; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db-example-asia";
    };
};

view "default-view" {
    match-clients { region-default; };
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db-example-default";
    };
};

Each zone file contains the region-specific records:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// /etc/bind/zones/db-example-us
$TTL 3600
@   IN  SOA ns1.example.com. admin.example.com. (
                2026041901  ; Serial
                3600        ; Refresh
                900         ; Retry
                604800      ; Expire
                3600 )      ; Minimum TTL

    IN  NS  ns1.example.com.
@   IN  A   203.0.113.10
www IN  A   203.0.113.10
api IN  A   203.0.113.11

Docker Setup for BIND9

BIND9 doesn’t have an official Docker image, but the Ubuntu-based image from Ubuntu/ISC works well:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# docker-compose.yml
version: '3.8'

services:
  bind9:
    image: ubuntu/bind9:latest
    environment:
      - BIND9_USER=root
    volumes:
      - ./named.conf:/etc/bind/named.conf:ro
      - ./named.conf.options:/etc/bind/named.conf.options:ro
      - ./zones:/etc/bind/zones:ro
      - ./data:/var/cache/bind
    ports:
      - "53:53"
      - "53:53/udp"
    restart: unless-stopped
    cap_add:
      - NET_BIND_SERVICE

Managing GeoIP ACLs in BIND9

The main challenge with BIND9 views is maintaining the IP-to-region ACL mappings. Since BIND9 doesn’t have built-in GeoIP lookup, you need to pre-compute CIDR blocks for each region. Several tools help with this:

  • ip2location CIDR generator: Converts GeoIP databases to BIND-compatible ACL files
  • maxmind-geoip2-bind: Script that reads GeoLite2 databases and outputs named.conf ACL blocks
  • Custom Python scripts: Parse GeoIP CSV exports and generate CIDR ranges

A typical workflow is to regenerate the ACL file weekly from an updated GeoIP database and reload BIND9 with rndc reload.

CoreDNS with GeoIP Plugin

CoreDNS is a modern, cloud-native DNS server written in Go. Its plugin architecture makes it highly extensible, and the geoip plugin adds geographic DNS routing capabilities.

Key stats (as of April 2026): 13,997 GitHub stars, last updated April 16, 2026. Written in Go. Now a graduated CNCF project.

How CoreDNS GeoIP Works

CoreDNS’s geoip plugin reads a MaxMind GeoIP database and uses it to determine the geographic origin of each query. Combined with the rewrite or hosts plugins, you can return different records based on the detected region.

Corefile Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
example.com {
    geoip {
        database /usr/share/GeoIP/GeoLite2-Country.mmdb
    }

    # Rewrite responses based on GeoIP country code
    rewrite {
        # US queries get US IP
        answer name example.com example.com
        answer type A
        answer data (
            country US 203.0.113.10
            country GB 198.51.100.10
            country DE 198.51.100.20
            country JP 192.0.2.10
            default 203.0.113.10
        )
    }

    # Fallback records
    hosts {
        203.0.113.10 example.com
        203.0.113.10 www.example.com
        fallthrough
    }

    log
    errors
}

Docker Compose for CoreDNS

CoreDNS ships as a single binary and has an official Docker image:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# docker-compose.yml
version: '3.8'

services:
  coredns:
    image: coredns/coredns:latest
    command: -conf /etc/coredns/Corefile
    volumes:
      - ./Corefile:/etc/coredns/Corefile:ro
      - ./geoip:/usr/share/GeoIP:ro
    ports:
      - "53:53"
      - "53:53/udp"
    restart: unless-stopped
    read_only: true
    security_opt:
      - no-new-privileges:true

CoreDNS is the simplest to deploy of the three options — it’s a single binary, starts in milliseconds, and uses minimal memory. The plugin ecosystem also means you can chain GeoDNS with rate limiting, caching, and health checking in the same server.

Comparison Table

FeaturePowerDNS GeoIPBIND9 ViewsCoreDNS GeoIP
GeoIP methodNative GeoIP backendManual CIDR ACLsGeoIP plugin
Database supportMaxMind, DB-IPExternal CIDR mappingMaxMind GeoLite2
GranularityCountry, continentCIDR-basedCountry, continent
Dynamic updatesAPI-driven, live reloadRequires rndc reloadHot-reload Corefile
Setup complexityMediumHigh (manual ACLs)Low
PerformanceExcellentExcellentExcellent
Memory usage~100-200 MB~50-150 MB~20-50 MB
Web UI / APIYes (built-in REST API)NoNo (plugins available)
Docker imageOfficialCommunity (ubuntu/bind9)Official
Cloud-nativeNoNoYes (CNCF graduated)
Best forLarge deploymentskubernetesackendsLegacy DNS infra, enterpriseKubernetes, microservices
GitHub stars4,34573713,997
LanguageC++CGo

Choosing the Right Solution

Pick PowerDNS GeoIP if:

  • You need a battle-tested authoritative DNS server with database-backed zones
  • You want built-in API access for automated zone management
  • You need multi-backend support (GeoIP + SQL + zone files simultaneously)
  • You’re running a large-scale DNS infrastructure with complex zone requirements

Pick BIND9 Views if:

  • You’re already running BIND9 and want to add GeoDNS without introducing a new server
  • You need maximum compatibility with existing DNS tooling and monitoring
  • Your organization has BIND9 expertise and standardized on ISC software
  • You need fine-grained control over IP-to-region mappings at the CIDR level

Pick CoreDNS GeoIP if:

  • You’re running Kubernetes or a cloud-native stack
  • You want the simplest deployment (single binary, minimal config)
  • You need to chain DNS features (GeoDNS + caching + rate limiting + health checks)
  • Memory footprint matters — CoreDNS uses 2-5x less RAM than PowerDNS or BIND9

Practical Deployment Tips

GeoIP Database Updates

All GeoDNS solutions depend on the accuracy of your GeoIP database. Set up automated updates:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# /etc/cron.weekly/update-geoip.sh
# Update MaxMind GeoLite2 database weekly

GEOLITE_URL="https://download.maxmind.com/app/geoip_download"
LICENSE_ID="your_license_id"
LICENSE_KEY="your_license_key"

curl -L -o /tmp/GeoLite2-Country.tar.gz \
  "${GEOLITE_URL}?edition_id=GeoLite2-Country&license_key=${LICENSE_KEY}"

tar -xzf /tmp/GeoLite2-Country.tar.gz -C /tmp
mv /tmp/GeoLite2-Country_*/GeoLite2-Country.mmdb /usr/share/GeoIP/
rm -rf /tmp/GeoLite2-Country_*

# Reload the DNS server
systemctl reload pdns    # PowerDNS
# OR: rndc reload        # BIND9
# OR: kill -HUP $(pidof coredns)  # CoreDNS

Health Check Integration

GeoDNS becomes powerful when combined with regional health checks. If your Tokyo endpoint goes down, you want Asian traffic to fail over to Singapore or the US automatically. CoreDNS’s health and rewrite plugins can implement this pattern. PowerDNS offers the lua backend for custom health check logic.

Testing Your GeoDNS Setup

Verify that queries from different regions return the correct IPs:

1
2
3
4
5
6
7
# Test from a specific IP (simulated)
dig @your-dns-server example.com +short

# Use dig with EDNS Client Subnet to simulate regional queries
dig @your-dns-server example.com +subnet=203.0.113.0/24 +short
dig @your-dns-server example.com +subnet=198.51.100.0/24 +short
dig @your-dns-server example.com +subnet=192.0.2.0/24 +short

Each subnet query should return the IP address associated with that geographic region.

FAQ

What is GeoDNS and how does it differ from regular DNS?

Regular DNS returns the same IP address for every user regardless of location. GeoDNS (geographic DNS) examines the source IP of each query and returns different IP addresses based on the user’s geographic location. This enables you to route European users to a Frankfurt data center, Asian users to Tokyo, and US users to Virginia — all through standard DNS resolution.

How accurate is GeoDNS routing?

Country-level GeoIP accuracy is typically 95-99%. City-level accuracy ranges from 50-80%. For GeoDNS routing, country or continent-level granularity is recommended because it provides the best balance of accuracy and coverage. The accuracy depends on the GeoIP database you use — MaxMind GeoLite2 is the most popular free option.

Can I use GeoDNS with a CDN?

Yes. In fact, GeoDNS and CDNs complement each other well. GeoDNS routes users to the nearest regional entry point, and the CDN handles caching and distribution within that region. You can also use GeoDNS as a CDN alternative for simpler setups, routing traffic directly to your own regional servers.

How often should I update the GeoIP database?

Weekly updates are recommended. IP address allocations change regularly as ISPs reassign addresses and new ranges come online. MaxMind releases GeoLite2 database updates weekly. Set up a cron job to download the latest database and reload your DNS server automatically.

What happens if a user’s IP is not in any defined region?

All three solutions support a default or fallback region. If a query’s IP doesn’t match any defined country or ACL, the DNS server returns the default record set. Always configure a sensible default — typically pointing to your largest or most cost-effective data center — so that unmapped traffic still gets served.

Is self-hosted GeoDNS better than Cloudflare or AWS Route 53?

It depends on your scale and needs. Cloudflare and Route 53 are easier to set up and include health checks, but they charge per million DNS queries. At 100+ million queries per month, self-hosting becomes significantly cheaper. Self-hosted GeoDNS also gives you full control over routing logic, no vendor lock-in, and the ability to integrate with internal systems.

Can I combine GeoDNS with load balancing?

Absolutely. GeoDNS handles the first layer — routing users to the right region. Within each region, you can use standard load balancers (HAProxy, NGINX, Envoy) to distribute traffic across multiple servers. This two-tier approach (GeoDNS → regional load balancer → backend servers) is the architecture used by many large-scale web platforms.

Advertise here