Run RFC 2136 dynamic DNS zones, for dynamic IP home connections or ACME DNS-01 challenges
  • Shell 53%
  • Python 25.6%
  • Jinja 14.8%
  • Dockerfile 6.6%
Find a file
Mike Gerber 09216dacff
Some checks failed
Container CI / test (push) Has been cancelled
📝 README: Creating keys + Testing
2026-10-22 21:37: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 🚧 test: run-locally.sh 2026-10-22 09:38:11 +02:00
README.md 📝 README: Creating keys + Testing 2026-10-22 21:37: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.

DNS delegation

For public DNS use, configure the appropriate NS records at your parent zone or DNS provider to delegate the desired zone to this server.

ACME

You can use a DNS CNAME record to delegate an ACME DNS-01 challenge to a record served by this service, allowing certificate validation without modifying the original zone's TXT records.

E.g.

_acme-challenge.example.com. CNAME _acme-challenge.acme.example.com.

Creating keys

TSIG secrets must be base64-encoded keys. Generate a key using:

tsig-keygen -a hmac-sha512 dyndns

Repeat for each key and copy the generated secret values into the YAML configuration. Note that the key name must match on client and server.

Keep this configuration private: It contains credentials that authorize DNS updates.

Testing

Query the authoritative server:

dig @127.0.0.1 dyn.example.com SOA

To test a dynamic update, create a TSIG key file containing the same key and secret as configured above:

key "dyndns" {
    algorithm hmac-sha512;
    secret "BASE64_ENCODED_TSIG_SECRET";
};

Then run:

nsupdate -k dyndns.key

Enter:

server 127.0.0.1
zone dyn.example.com
update add home.dyn.example.com. 60 A 192.0.2.123
send

Verify the record:

dig @127.0.0.1 home.dyn.example.com A +short

Expected result:

192.0.2.123

The server supports standard RFC 2136 clients, including nsupdate and ACME clients with RFC 2136 DNS provider support.

Notes

  • The server is authoritative only. Recursive DNS queries are disabled.
  • DNS zone transfers are disabled.
  • Dynamic update permissions are restricted to the configured record names.
  • A separate TSIG key can be assigned to each record name.
  • Zone files are initialized automatically if they do not exist.
  • Existing zone files are preserved on restart.
  • The image includes a health check that queries the local DNS server without depending on any configured zone.