hobbydns is a flexible, developer-friendly private DNS server designed for home labs, internal corporate networks, and fast-prototyping environments.
Unlike traditional heavyweight DNS software (like BIND9 or Unbound) which require rigid zone structures and full service restarts to apply changes, hobbydns exposes everything via a unified, simple JSON schema. By running an interactive runtime hot-swap routine, you can modify subdomains, add entirely new top-level internal zones, or toggle your global recursive proxy logic on the fly without breaking a single active network connection.
- πΊοΈ Multi-Zone Authorization: Host completely distinct internal environments (e.g.,
.lan,.internal, or local company subdomains, or even.comdomains) simultaneously. - β‘ Conditional Upstream Proxy Forwarding: Acts as a fallback proxy for public domains (like
github.com) by fetching them from an upstream resolver. Toggle it on and off instantly in the config. - π In-Memory Dynamic Hot-Swapping: Type
reloadin the runtime CLI to reload configuration data safely using thread-safe state synchronization (sync.RWMutex). - πͺΆ Lean Architecture: Built using Go, making it trivial to compile into a single, cross-platform executable binary file.
Download the executable for your system architecture:
- Linux amd64: hobbydns-linux-amd64
- Linux arm64: hobbydns-linux-arm64
- macOS amd64: hobbydns-darwin-amd64
- macOS arm64: hobbydns-darwin-arm64
- Windows amd64: hobbydns-windows-amd64.exe
- Windows arm64: hobbydns-windows-arm64.exe
Ensure you have a config.json placed in your working directory (see structural example below). Because binding to standard system network ports (:53) requires admin network capabilities, execute with escalated privileges:
sudo ./hobbydnsOr, if you're not using a system port, just run it in user mode:
./hobbydnsRelease images are published to GitHub Container Registry as ghcr.io/lllincoln/hobbydns.
Pull the latest release image:
docker pull ghcr.io/lllincoln/hobbydns:latestRun the container with your local config.json mounted into /data, which is the container working directory:
docker run --rm -it \
--name hobbydns \
-p 53:53/tcp \
-p 53:53/udp \
-v "./config.json:/data/config.json:ro" \
ghcr.io/lllincoln/hobbydns:latestTo use a specific release, replace latest with the release tag:
docker run --rm -it \
--name hobbydns \
-p 53:53/tcp \
-p 53:53/udp \
-v "./config.json:/data/config.json:ro" \
ghcr.io/lllincoln/hobbydns:v1.0.0If port 53 requires elevated permissions on your host, run Docker with the appropriate privileges for your environment.
Create your control rules inside a file named config.json. Below is a comprehensive blueprint detailing global options, fallback variables, and multi-zone arrays:
{
"host": "0.0.0.0:53",
"proxy_fallback": true,
"upstream_dns": "1.1.1.1:53",
"zones": [
{
"zone_name": "internal.example.com.",
"master_ns": "ns1.internal.example.com.",
"records": {
"": [
{ "type": "A", "ttl": "5m", "values": [{ "address": "10.0.0.10" }] }
],
"ns1": [
{ "type": "A", "ttl": "5m", "values": [{ "address": "10.0.0.10" }] }
],
"devbox": [
{ "type": "A", "ttl": "5m", "values": [{ "address": "10.0.0.100" }] }
]
}
},
{
"zone_name": "example.com.",
"master_ns": "ns1.example.com.",
"records": {
"ns1": [
{ "type": "A", "ttl": "5m", "values": [{ "address": "10.0.0.10" }] }
],
"api": [
{ "type": "A", "ttl": "2m", "values": [{ "address": "10.0.0.150" }] }
],
"staging": [
{ "type": "CNAME", "ttl": "10m", "values": [{ "address": "devbox.internal.example.com." }] }
]
}
}
]
}When changes are applied to your config.json file, open the interactive application shell, type reload, and hit Enter! No restarts needed:
Running dynamic private DNS on 0.0.0.0:53...
Server engine online. Type 'reload' anytime to apply configurations.
reload
π Configuration and Proxy parameters hot-swapped successfully!
When running in Docker, the command above uses a read-only bind mount for config.json. Update the host file, then type reload in the attached container terminal.
Open a separate terminal shell and execute network diagnostic lookups via dig to confirm your custom domain mappings work beautifully:
# Local internal zone query π₯οΈ
dig devbox.internal.example.com @127.0.0.1
# External recursive fallback proxy query π
dig github.com @127.0.0.1By default, modern systemd-based Linux systems run an internal stub resolver via systemd-resolved which blocks port 53. Check for conflicts:
sudo lsof -i :53If active, stop it before executing your server:
sudo systemctl stop systemd-resolvedAs a reminder, you can also change the port that hobbydns runs on.
- Multi-zone definition support
- Recursive fallback proxying for external routing requests
- Interactive CLI hot-swapping runtime loop
- Add an automated system file-watcher (
fsnotify) to handle live reloads instantly on file save without typing - Implement integrated HTTP metrics endpoint reporting query statistics and error codes
- Add local database persistent storage fallbacks via SQLite
- Support more DNS record types
Create an issue if you want to see something new or have a bug that needs to be fixed!
Don't be afraid to contribute! Whether it's creating an issue or submitting a PR, we are nice people and welcoming to beginners.
Here's how to get started:
- Fork the project
- Clone your fork
- Create your feature branch (
git checkout -b feature/your-amazing-feature) - Download project dependencies (
go mod download) - Build the project (
go build) - Commit your changes (
git commit -m 'Add an amazing feature') - Push to the branch (
git push origin feature/your-amazing-feature) - Open a pull request and tell us what your amazing feature is
Distributed under the MIT License. See LICENSE.txt for more details.
π Lincoln Maxwell: https://lincolnmaxwell.com
π Project Link: https://github.com/lllincoln/hobbydns
- miekg/dns - Comprehensive, robust DNS library for Go
- 256dpi/newdns - Clean authoritative DNS server framework
- Best-README-Template - For the gorgeous markdown layout
- Our contributors: