El balanceo de carga geográfico por DNS (GeoDNS) responde a la misma consulta con direcciones distintas según desde dónde se haga. Si tienes tu aplicación desplegada en Europa y en Norteamérica, un visitante de Madrid recibirá la IP del servidor europeo y uno de Houston la del americano, lo que reduce la latencia y reparte la carga entre regiones. En esta guía configurarás un servidor DNS autoritativo BIND 9 en Ubuntu 24.04 que usa la base de datos GeoLite2 de MaxMind y vistas por continente para servir registros diferentes a cada región.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS que hará de DNS autoritativo, por ejemplo un VPS de CubePath. En producción necesitarás al menos dos, idealmente en regiones distintas.
  • Un usuario no root con privilegios sudo.
  • Un dominio (your_domain) cuya zona puedas delegar a tus propios servidores de nombres en el registrador.
  • Al menos dos servidores con tu aplicación en regiones distintas. En los ejemplos: 203.0.113.10 (Europa) y 198.51.100.10 (Norteamérica).
  • Una cuenta gratuita de MaxMind para descargar GeoLite2 y una clave de licencia generada en su portal.
  • El puerto 53 abierto por TCP y UDP.

Cómo funciona y qué limitaciones tiene

Un servidor autoritativo no ve la IP del usuario final, sino la del resolvedor recursivo que le pregunta (el de su operador, el de su empresa o uno público). BIND busca esa IP en la base GeoIP, la asigna a una vista y responde con la zona de esa vista. Normalmente el resolvedor está cerca del usuario, así que el resultado es correcto, pero hay dos consecuencias a tener en cuenta:

  • Un usuario que usa un resolvedor lejano recibirá la región de ese resolvedor.
  • Los resolvedores guardan la respuesta en caché durante el TTL del registro. Usa TTL cortos (60 a 300 segundos) en los registros geográficos para que los cambios se apliquen rápido.

BIND no comprueba si los servidores de destino están vivos. El GeoDNS decide por ubicación; la conmutación por fallo requiere cambiar la zona o usar otro mecanismo, como se explica al final.

Paso 1: Instalar BIND

sudo apt update
sudo apt install bind9 bind9-utils bind9-dnsutils

El paquete de Ubuntu está compilado con soporte para las bases MaxMind GeoIP2 (libmaxminddb). Compruébalo:

named -V | grep -i 'linked to maxminddb'
linked to maxminddb version: 1.9.1

Si aparece esa línea (la versión puede variar), BIND puede usar ACL con geoip. Comprueba también que el servicio está activo:

systemctl status named --no-pager

Paso 2: Descargar la base GeoLite2

La herramienta oficial geoipupdate descarga y actualiza las bases de MaxMind:

sudo apt install geoipupdate

Edita su configuración con tu ID de cuenta y tu clave de licencia:

sudo nano /etc/GeoIP.conf
AccountID your_account_id
LicenseKey your_license_key
EditionIDs GeoLite2-Country
DatabaseDirectory /usr/share/GeoIP

Protege el archivo, porque contiene tu clave, y descarga la base:

sudo chmod 600 /etc/GeoIP.conf
sudo mkdir -p /usr/share/GeoIP
sudo geoipupdate -v
ls -l /usr/share/GeoIP/
-rw-r--r-- 1 root root 9453010 Sep 25 10:12 GeoLite2-Country.mmdb

MaxMind actualiza GeoLite2 dos veces por semana. Programa la actualización con cron:

echo '17 4 * * 3,6 root /usr/bin/geoipupdate && /usr/sbin/rndc reload >/dev/null' | sudo tee /etc/cron.d/geoipupdate-bind

rndc reload hace que BIND vuelva a abrir la base tras la descarga.

Paso 3: Configurar las opciones globales

Este servidor será solo autoritativo, sin recursión, y debe saber dónde está la base GeoIP. Edita las opciones:

sudo nano /etc/bind/named.conf.options
options {
    directory "/var/cache/bind";
    geoip-directory "/usr/share/GeoIP";

    recursion no;
    allow-transfer { none; };

    listen-on { any; };
    listen-on-v6 { any; };
};

Paso 4: Definir ACL por región y vistas

Cuando usas vistas en BIND, todas las zonas deben estar dentro de alguna vista. Ubuntu incluye por defecto named.conf.default-zones fuera de las vistas, lo que provoca un error al arrancar. Como este servidor no hace recursión, esas zonas no son necesarias. Edita el archivo principal:

sudo nano /etc/bind/named.conf

Comenta la línea de las zonas por defecto para que quede así:

include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
//include "/etc/bind/named.conf.default-zones";

Ahora define las regiones y las vistas en named.conf.local:

sudo nano /etc/bind/named.conf.local
acl "region-eu" {
    geoip continent EU;
    geoip continent AF;
};

acl "region-na" {
    geoip continent NA;
    geoip continent SA;
};

view "eu" {
    match-clients { region-eu; };
    zone "your_domain" {
        type primary;
        file "/etc/bind/zones/db.your_domain.eu";
    };
};

view "na" {
    match-clients { region-na; };
    zone "your_domain" {
        type primary;
        file "/etc/bind/zones/db.your_domain.na";
    };
};

view "default" {
    match-clients { any; };
    zone "your_domain" {
        type primary;
        file "/etc/bind/zones/db.your_domain.default";
    };
};

Las vistas se evalúan en orden y gana la primera que coincide, así que la vista default con any debe ir la última: recoge Asia, Oceanía y las IP que no aparecen en la base. Los códigos de continente son de dos letras: AF, AN, AS, EU, NA, OC y SA. Si prefieres afinar por país, usa geoip country ES; con el código ISO del país.

Paso 5: Crear los archivos de zona

Crea el directorio y la zona para Europa:

sudo mkdir -p /etc/bind/zones
sudo nano /etc/bind/zones/db.your_domain.eu
$TTL 3600
@       IN SOA  ns1.your_domain. hostmaster.your_domain. (
                2026092501 ; serial
                3600       ; refresh
                600        ; retry
                1209600    ; expire
                300 )      ; negative cache TTL

@       IN NS   ns1.your_domain.
@       IN NS   ns2.your_domain.
ns1     IN A    192.0.2.53
ns2     IN A    192.0.2.54

@   60  IN A    203.0.113.10
www 60  IN A    203.0.113.10

Crea la de Norteamérica partiendo de la anterior y cambia solo las IP de la aplicación:

sudo cp /etc/bind/zones/db.your_domain.eu /etc/bind/zones/db.your_domain.na
sudo sed -i 's/203\.0\.113\.10/198.51.100.10/' /etc/bind/zones/db.your_domain.na

La zona por defecto puede apuntar a la región que mejor atienda al resto del mundo. En este ejemplo, a Europa:

sudo cp /etc/bind/zones/db.your_domain.eu /etc/bind/zones/db.your_domain.default

Los registros NS, SOA y de correo deben ser idénticos en todas las variantes; solo cambian los registros que quieres geolocalizar. Sustituye 192.0.2.53 y 192.0.2.54 por las IP reales de tus servidores de nombres.

Paso 6: Validar y cargar la configuración

Comprueba la sintaxis global y cada zona antes de recargar:

sudo named-checkconf
sudo named-checkzone your_domain /etc/bind/zones/db.your_domain.eu
sudo named-checkzone your_domain /etc/bind/zones/db.your_domain.na
sudo named-checkzone your_domain /etc/bind/zones/db.your_domain.default
zone your_domain/IN: loaded serial 2026092501
OK

named-checkconf no imprime nada si todo es correcto. Reinicia BIND y revisa el log:

sudo systemctl restart named
sudo journalctl -u named -n 30 --no-pager

Debes ver una línea zone your_domain/IN/eu: loaded serial 2026092501 por cada vista y ningún error sobre GeoIP. Abre el puerto 53:

sudo ufw allow 53/udp
sudo ufw allow 53/tcp

Paso 7: Comprobar las respuestas por región

Una consulta desde el propio servidor viene de 127.0.0.1, que no está en la base GeoIP, así que debe caer en la vista default:

dig @127.0.0.1 www.your_domain A +short
203.0.113.10

La prueba real se hace consultando directamente a tu servidor desde máquinas situadas en cada región, por ejemplo un VPS en Europa y otro en Norteamérica:

dig @your_server_ip www.your_domain A +short

Desde Norteamérica la respuesta debe ser 198.51.100.10 y desde Europa 203.0.113.10. Si solo tienes acceso a una región, herramientas de medición distribuida como Globalping o RIPE Atlas permiten lanzar consultas DNS desde sondas en otros países.

Cuando todo responda como esperas, crea en tu registrador los registros de pegamento (glue) para ns1.your_domain y ns2.your_domain y delega el dominio en ellos. El segundo servidor de nombres debe tener exactamente la misma configuración de vistas y zonas.

Conmutación por fallo

BIND no sabe si 203.0.113.10 ha dejado de responder. Tienes varias opciones:

  • Manual o con tu monitorización: cuando una región cae, cambia la IP en la zona afectada, incrementa el serial y ejecuta sudo rndc reload. Con TTL de 60 segundos el cambio se propaga en pocos minutos.
  • Varias IP por región: publica dos registros A en cada vista (por ejemplo, un servidor principal y uno de otra región). Los navegadores prueban la siguiente dirección si la primera no conecta, aunque con cierto retraso.
  • PowerDNS con registros LUA: PowerDNS Authoritative permite registros que comprueban la disponibilidad antes de responder, por ejemplo www IN LUA A "ifportup(443, {'203.0.113.10', '198.51.100.10'})" con enable-lua-records=yes en su configuración. Si necesitas geolocalización y comprobaciones de salud en la misma pieza, es una opción a valorar.

Solución de problemas

  • when using 'view' statements, all zones must be in views. Queda alguna zona fuera de las vistas, normalmente por el include de named.conf.default-zones. Coméntalo como en el paso 4.
  • Errores sobre GeoIP en journalctl -u named. Revisa que /usr/share/GeoIP/GeoLite2-Country.mmdb existe. Si el log muestra Permission denied, AppArmor está bloqueando la lectura: añade la línea /usr/share/GeoIP/** r, a /etc/apparmor.d/local/usr.sbin.named y recarga el perfil con sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.named.
  • Todas las consultas reciben la vista default. La base no se ha cargado o las ACL no coinciden. Comprueba el continente de una IP concreta con mmdblookup, del paquete mmdb-bin: mmdblookup --file /usr/share/GeoIP/GeoLite2-Country.mmdb --ip 198.51.100.10 continent code.
  • Los cambios no se ven. Olvidaste incrementar el serial o los resolvedores aún tienen la respuesta en caché. Consulta directamente a tu servidor con dig @your_server_ip para descartar la caché.

Conclusión

Tu servidor BIND responde ahora a cada región con la IP de su centro de datos más cercano, usando GeoLite2 actualizado automáticamente y TTL cortos para poder reaccionar ante cambios. Como siguientes pasos, replica la configuración en un segundo servidor de nombres (con transferencias de zona por vista protegidas con TSIG o gestionando los archivos con Ansible), firma la zona con DNSSEC y conecta tu monitorización para automatizar el cambio de IP cuando una región deje de responder.