Run RFC 2136 dynamic DNS zones, for dynamic IP home connections or ACME DNS-01 challenges
  • Shell 50.7%
  • Python 26.9%
  • Jinja 15.5%
  • Dockerfile 6.9%
Find a file
Mike Gerber 0516ac2852
Some checks failed
Container CI / test (push) Has been cancelled
📝 README: Use dyn.example.com
2026-10-12 19:29:56 +02:00
.github/workflows 📦 Explicitly name platform in test action 2026-10-12 13:00:22 +02:00
container 🧹 No need for fqdn in zone records 2026-10-12 10:47:11 +02:00
test 🧹 No need for fqdn in zone records 2026-10-12 10:47:11 +02:00
README.md 📝 README: Use dyn.example.com 2026-10-12 19:29:56 +02:00

RFC 2136 DNS Container

A containerized authoritative DNS server with support for dynamic DNS updates using RFC 2136.

Useful for:

  • Dynamic DNS: Update DNS records for hosts with changing IP addresses, such as home internet connections.
  • ACME DNS-01 challenges: Allow ACME clients to create and remove TXT records for certificate validation.

The container runs BIND 9 and generates its configuration from a simple YAML file. DNS updates are authenticated using TSIG (HMAC-SHA512), with permissions restricted to individual DNS record names.

Configuration

Create a file named config.yml:

soa_mname: ns-dyn.example.com
soa_rname: hostmaster.example.com

keys:
  dyndns: "BASE64_ENCODED_TSIG_SECRET"
  acme: "ANOTHER_BASE64_ENCODED_TSIG_SECRET"

zones:
  dyn.example.com:
    home: dyndns
  acme.example.com:
    _acme-challenge: acme

This configuration:

  • Creates authoritative DNS zones for dyn.example.com and acme.example.com.
  • Allows the dyndns key to update home.dyn.example.com.
  • Allows the acme key to update _acme-challenge.acme.example.com.

The soa_mname and soa_rname settings specify the primary authoritative nameserver (= the server this is running on) and responsible party for the SOA record. For soa_rname, use the DNS representation of an email address (hostmaster.example.com corresponds to hostmaster@example.com).

Docker Compose

Create a docker-compose.yml:

services:
  dns:
    image: ghcr.io/mikegerber/rfc2136-dns-container:main
    restart: unless-stopped

    ports:
      - "53:53/udp"
      - "53:53/tcp"

    volumes:
      - ./config.yml:/etc/bind/rfc2136-dns-container.yml:ro
      - dns-data:/var/bind/rfc2136-dns-container

volumes:
  dns-data:

Start the server:

docker compose up -d

Check the logs:

docker compose logs -f dns

Port 53 must be available on the host for both UDP and TCP.

The dns-data volume stores zone files and dynamic updates, preserving DNS records across container restarts and upgrades.