dnsmasq — names on your own network
dnsmasq answers names on your own network: wildcard zones that point a whole subdomain at one address, static records for machines that have no DNS entry, and a cache in front of an upstream resolver. One container.
Last run on a host 8 Nov 2026
Pinned at 2.93 (released 20 Aug 2026) · the newest upstream release
Needs: a host where port 53 is free, and — if it should answer over a VPN — that interface present.
This stack sits outside the usual pattern in two ways. It uses host networking, because it binds directly to the interfaces it serves. And it is not behind Traefik: it is the name layer, so nothing routes to it by hostname.
Installation
Section titled “Installation”From core/dnsmasq/:
-
Copy the environment file:
Terminal window cp .env.example .envSet the interfaces it listens on —
loand the VPN interface are the shipped defaults — the upstream resolvers, and the wildcard zones:DNS_INTERFACE_1=loDNS_INTERFACE_2=tailscale0DNS_WILDCARD_1_DOMAIN=test.lab.example.comDNS_WILDCARD_1_IP=100.100.100.10It listens on the named interfaces only, not on everything the host has.
-
Add static records, if any, to
config/records.hosts:100.100.100.10 nas.lab.example.com100.100.100.20 printer.lab.example.com -
Render the configuration. The container mounts the result read-only, and editing
config/dnsmasq.confdirectly is overwritten by the next render:Terminal window ./ops/scripts/render.sh -
Start it:
Terminal window docker compose up -ddocker compose logs -f
Verify
Section titled “Verify”nslookup anything.test.lab.example.com 127.0.0.1 # the wildcard answersnslookup nas.lab.example.com 127.0.0.1 # a static recordnslookup example.com 127.0.0.1 # forwarding upstream worksAll three matter separately: a wildcard can answer while forwarding is broken, and the reverse.
Going further
Section titled “Going further”Applying a change
Section titled “Applying a change”Which change needs which action:
| Changed | Apply with |
|---|---|
config/records.hosts |
docker exec dnsmasq kill -HUP 1 — re-reads records and clears the cache, no downtime |
wildcard zones, upstream resolvers, interfaces in .env |
./ops/scripts/render.sh then docker compose restart |
Wildcards and upstream forwarding cover AAAA records as well. A machine with both
gets two lines in config/records.hosts, one per family:
fd7a:115c:a1e0::10 nas.lab.example.com100.100.100.10 nas.lab.example.comBackup
Section titled “Backup”The rendered configuration comes from .env and the template, and
config/records.hosts is the only file written by hand. Both belong in version
control rather than in a backup archive; restoring means checking out the
repository, running the render script and starting the container.
Updates
Section titled “Updates”-
Raise
APP_TAGin.env. -
Terminal window docker compose pull && docker compose up -d -
Re-run the three checks from Verify. A configuration directive removed upstream shows up as a container that starts and answers nothing.
Troubleshooting
Section titled “Troubleshooting”Nothing answers on the VPN address. The interface is not in the
DNS_INTERFACE_* list, or it did not exist when the container started.
A record change has no effect. records.hosts needs the reload signal;
.env needs a re-render and a restart. The two are not interchangeable.
Port 53 is already in use. A resolver on the host holds it —
systemd-resolved is the usual one. It has to release the port before this
container can bind it.
Repository files
Section titled “Repository files”core/dnsmasq/README.md— setup, wildcard zones, reload behaviourcore/dnsmasq/UPSTREAM.md— source and upgrade checklist
Official sources
Section titled “Official sources”© 2026 SecDockBlueCode Apache-2.0Site content CC BY-NC 4.0