KyDNS

KyDNS

apps.detail.types.app from Yoshiofthewire's Repository

apps.detail.sections.overview

KyDNS is a self-hosted local DNS server and service directory for homes, homelabs, and small teams. Register a service once and it answers on your private domain (home.arpa by default) with address records, aliases, and derived reverse (PTR) records — no hosts files, no opaque rewrite rules. It also does per-subnet views, dnsmasq DHCP lease discovery, service health checks, opt-out blackhole filtering, and upstream forwarding over DNS-over-TLS or DNS-over-HTTPS with a cache and failover. Queries are default-closed: anything outside the allowed networks gets REFUSED, so an accidentally exposed KyDNS is not an open resolver. IMPORTANT — this template uses a custom br0 network so the container gets its own LAN address and owns port 53 there, without fighting Unraid's host DNS or an existing AdGuard Home / Pi-hole. Set "Fixed IP address" below to an address inside your LAN subnet and OUTSIDE your router's DHCP pool. Do not leave it blank: Docker then picks the first free address in the subnet for you — usually x.x.x.2 — which will collide with a device you already have or sit inside the DHCP pool, and KyDNS answers nowhere. No config file needed to start: the image ships a working one. Set the fixed IP, apply, and it serves. Mapping your own kydns.yaml is optional and lives under Advanced, for settings this template does not expose (upstream resolvers, private domain, reverse zones). AFTER FIRST START: read the setup token from the log (or from setup-token in the data directory) and finish setup at the WebUI. KyDNS speaks plain HTTP — put a TLS-terminating reverse proxy in front if you expose it. Note: macvlan/ipvlan networks deliberately block traffic between the Unraid host and its own containers. Everything else on the LAN reaches KyDNS fine; only the host itself cannot.

KyDNS

KyDNS is a self-hosted local DNS server and service directory for homes, homelabs, and small teams.

Its primary job is to make private services easy to name and reach. It also provides opt-out DNS blackhole filtering with built-in and operator-managed lists.

Example

Register a service once:

kydns service add kypost \
  --address 192.168.1.20 \
  --alias webmail \
  --check https://kypost.home.arpa/health

Clients can then use:

kypost.home.arpa
webmail.home.arpa

without manually maintaining hosts files or opaque DNS rewrite rules.

What works today

  • First-class services, each with its own address records and aliases, plus derived reverse records. Manual A, AAAA, and CNAME records for anything a service doesn't cover.
  • A configurable private domain, home.arpa by default.
  • Per-subnet views: one name can answer differently on the LAN and over a VPN.
  • DHCP lease discovery from dnsmasq, with promote-to-service.
  • Health checks over HTTP, HTTPS, and TCP, with status in the web interface.
  • Upstream forwarding over DNS-over-TLS and DNS-over-HTTPS, with a cache, sequential failover, and single-flight.
  • A web UI, a JSON API, and a CLI that talks to the API.
  • Server settings — upstreams, the query ACL, TTLs, cache bounds, logging, discovery and health — edited from any of the three and applied without a restart. Only five keys are still in a config file.
  • Linked-server replication, operable from the CLI and the web UI: pair two nodes by confirming a fingerprint the way you would an SSH host key, watch each replica's lag on the Replication screen, and promote a replica with one command when the primary is gone. A replica follows its primary over its own pinned TLS listener, keeps answering DNS when the primary is down, refuses administrative writes with the address of the node to make them on, and reports service health as unknown rather than showing you what it last heard. See Replication.
  • Built-in and custom DNS blacklists, one-off allow/deny rules, and a one-button filtering toggle. Filtering is enabled by default and never overrides local records.
  • YAML and JSON import/export for backup and Git-based configuration.

Blacklist filtering

The web UI's Blacklists tab has a global on/off toggle, the built-in list (StevenBlack's unified hosts file, MIT licensed), any custom lists an operator adds by URL, and one-off allow/deny rules. Lists come in three formats: plain domains, hosts-file syntax, and Adblock Plus-style rules.

Precedence is deny beats allow beats lists: a deny rule always blocks, an allow rule always overrides a list, and lists only apply where no rule says otherwise. Matching is on label boundaries, so a rule or list entry for ads.example also blocks sub.ads.example but never badads.example.

A blocked name gets a local NXDOMAIN with the authoritative (AA) bit clear and never reaches an upstream resolver. A KyDNS service or manual record can never be blocked: the policy is only consulted after the authoritative lookup declines to answer.

This is domain filtering, not traffic inspection, parental control, or a malware guarantee.

Not yet

  • Automatic failover. Replication is one primary, chosen by you. Nothing elects a new one, nothing discovers peers, and a replica never promotes itself. Losing the primary costs administration, not resolution: give clients both servers and they keep resolving.
  • Local DNSSEC validation. KyDNS trusts the upstream's verdict over an encrypted channel; it does not verify signatures itself. An AD bit from KyDNS means "the resolver we talked to privately said it validated," not "KyDNS checked the chain."
  • Docker Compose discovery, and lease formats other than dnsmasq.
  • Local TLS certificate issuance for private service names.

Configuration

Three keys live in a file, and two more if you replicate. Everything else lives in the database and is edited in the UI.

Copy kydns.example.yaml to /etc/kydns/kydns.yaml, or point somewhere else with --config. These three are read from it at every start, because KyDNS needs them before it has a database or a web UI:

Setting Default Notes
data_dir required Holds the database and the bootstrap/setup tokens. Back it up.
dns.listen :53 UDP and TCP. Port 53 needs root or CAP_NET_BIND_SERVICE.
admin.listen 127.0.0.1:8053 Web UI and JSON API. Plain HTTP — proxy it for TLS.

Two more are read from the file only if you run more than one server: replication.listen and replication.primary. Setting both is a startup error. See Replication.

Changing one of those means restarting KyDNS. The Settings screen shows all three read-only, so you can see what the running server actually has.

Every other setting — the private domain, reverse zones, upstreams, allow_query, Tailscale, TTLs, the cache bounds, the two logging opt-ins, lease discovery, and the health-check intervals — lives in the database. kydns.example.yaml still lists them, but only as first-run seed values: they populate a fresh database and are ignored on every start after that. Editing them in the file later does nothing.

Edit them in one of three places, all of which go through the same validation and the same write path:

kydns settings get                       # what the server is running
kydns settings set ttl=120 log_queries=true
kydns settings set allow_query=192.168.1.0/24,10.0.0.0/8
curl -H "Authorization: Bearer $KYDNS_TOKEN" $KYDNS_URL/api/v1/settings
curl -X PATCH -H "Authorization: Bearer $KYDNS_TOKEN" \
  -d '{"ttl":120}' $KYDNS_URL/api/v1/settings

PATCH merges: keys you leave out keep their value, so two people editing different settings do not clobber each other. Settings are part of kydns export and are applied by kydns import.

Almost everything applies the moment it is saved, with no restart and no dropped queries: upstreams (which also flushes the cache), the private domain, reverse zones, allow_query, allow_tailscale, the TTL, all four cache settings, both log flags, the three health settings, and the discovery interval. One cannot change in a running process — dhcp_lease_file, which the discovery poller is opened against. It is saved anyway, and a banner names the running value and the saved one until you restart.

Renaming the private domain

Services are stored by short name and follow the zone on their own. Manual records are stored as full names, so changing private_domain moves them: printer.home.arpa. becomes printer.lan.example., and a CNAME or PTR pointing into the old zone is repointed with it. The Settings screen lists every record it would move and does nothing until you save a second time. The records and the setting are written in one transaction, so a rename cannot half-apply, and the cache is flushed because every cached name under the old zone is now wrong.

Records outside the private zone are left alone — they are yours, not ours to rewrite. Clients hold the old names until their TTL expires.

Opening allow_query beyond your LAN

Adding a range outside loopback, RFC1918, ULA, link-local and CGNAT would make KyDNS an open resolver, so it takes a deliberate second step: retype the prefix in its canonical masked form to confirm it. In the settings form that is the confirmation box, over the API it is confirm_public, and on the CLI it is --confirm-public:

kydns settings set allow_query=192.168.0.0/16,203.0.113.0/24 \
  --confirm-public 203.0.113.0/24

You are asked once, when the prefix is new. A prefix already stored counts as confirmed, so later saves of unrelated settings never ask again. While any public range is configured, the dashboard and the Settings screen carry a standing banner and KyDNS logs a warning naming the prefix at every start. See SECURITY.md for the full rule.

Upgrading from a config-file deployment

  • An existing kydns.yaml keeps working. On the first start after the upgrade its values seed the database, and from then on the database owns them. That includes a dns.allow_query that reaches past your LAN: it is grandfathered rather than refused, because refusing to start would take a working household's DNS offline over a file that was legal when it was written. It does log a warning naming the prefix at every start, and shows a standing banner, until you remove it.
  • Confirmation is asked once. Adding a public prefix through the UI, the API or the CLI asks you to retype it that one time. It does not re-ask on every later save.
  • A backup containing a public prefix cannot be restored. There is no confirmation path through import, so a document whose allow_query carries a public prefix the server is not already serving is refused. In replace mode that check runs before the destructive part, so the existing registry and settings are left exactly as they were rather than half-replaced. Edit the prefix out of the document, import it, then add the prefix back with kydns settings set ... --confirm-public.

Upstream encryption

The upstream list is edited under Settings, one entry per line, and applies as soon as it is saved. The scheme decides how much you trust the answer:

tls://1.1.1.1:853              # DNS-over-TLS
https://9.9.9.9/dns-query      # DNS-over-HTTPS
udp://192.168.1.1:53           # plain DNS, opted into per upstream

The host must be an IP address. A hostname would need DNS to resolve it, and KyDNS may be the thing resolving — on a machine that points at itself that is a loop. Cloudflare, Quad9 and Google all present certificates valid for their IPs, so nothing further is needed. When a provider's certificate needs a hostname, put it after a #:

tls://45.90.28.0:853#abc123.dns.nextdns.io

That sets the TLS server name while still dialling the address you gave. Certificate verification is always on and there is no option to turn it off.

With only encrypted upstreams, a query fails rather than falling back. If every one is unreachable, clients get SERVFAIL and the Settings screen names the upstream and the error — usually a firewall blocking port 853. That is the intended behaviour: silently dropping to plain DNS is exactly what an attacker who blocks 853 is hoping for.

The escape hatch is one line. Add a udp:// upstream and KyDNS will use it when the encrypted ones fail. Answers it serves have the authenticated-data flag cleared, because nothing authenticated them, and the dashboard carries a banner for as long as the entry is there.

KyDNS is not a validating resolver. It does not verify signatures, hold a trust anchor, or walk a chain of trust. It makes the path to a resolver that does all of that private and tamper-proof, and passes that resolver's verdict through.

Tailscale

Tailscale addresses are CGNAT (100.64.0.0/10), not RFC1918, so they are not covered by the default allow_query. Leave "Allow Tailscale addresses" off unless you use Tailscale — some ISPs also assign CGNAT addresses, which would make the range a standing exposure on a WAN-facing interface. It is a checkbox under Settings, or kydns settings set allow_tailscale=true, and it takes effect at once.

While it is off, tailnet clients get REFUSED. KyDNS says so rather than failing silently: the dashboard shows a banner, the Settings screen flags any view that can never match, and the startup log warns.

To give one name a LAN answer and a tailnet answer, add a view for 100.64.0.0/10 and give the service a second address tagged with it.

Putting a service behind a reverse proxy

A service speaking plain HTTP behind a proxy that terminates TLS wants two different addresses: clients should reach the proxy, monitoring should reach the service.

Register the service at its real address, set the proxy address, and tick "Send DNS to the proxy":

kydns service add kypost \
  --address 192.168.1.30 \
  --proxy 192.168.1.20 --via-proxy \
  --check http://192.168.1.30:8080/health

kypost.home.arpa now answers 192.168.1.20, and so does every alias. The reverse record for 192.168.1.30 still names kypost.home.arpa, so several services behind one proxy each keep a correct PTR.

Point the health check at the service, not the proxy. A proxy returning 502 for a dead backend still answers a prober perfectly, so checking the proxy tells you only that the proxy is up.

Routing can be turned on or off after the service is created, which is how you tell whether a problem is the application or the proxy: leave the address in place and just flip the switch. The Services screen has a routing form on each row — a proxy address field, a checkbox, and a Save button — that changes only those two fields.

The same is true over the API: PATCH /api/v1/services/{id} merges the body onto the existing service, so any field you leave out keeps its value. A body of

{"name": "grafana", "addresses": [{"address": "192.168.1.60"}]}

changes the name and address and leaves aliases, check_url, check_insecure, proxy_address, and route_via_proxy untouched. To clear a field explicitly, include it with an empty value ("aliases": [], "check_url": ""); an addresses or aliases array you do provide replaces the whole list rather than merging into it, so a routed address never inherits a view tag from the one it replaced.

A service with both an IPv4 and an IPv6 address behind one proxy answers A only: --proxy takes one address, so the AAAA query gets NODATA instead of the service's own IPv6 address. Give the service just the address family the proxy needs, or leave routing off if both must keep resolving.

Replication

A second KyDNS keeps resolving while the first one is down. One node is the primary and takes every change; the other follows it and serves the same answers read-only. Give your clients both addresses.

Both replication keys are owned by the config file, so each step here that sets one needs a restart. On the primary:

replication:
  listen: "192.168.1.10:8443"   # its own TLS listener, separate from DNS and the UI

On the node that will follow it:

replication:
  primary: "192.168.1.10:8443"

Restart both, then pair them. Pairing works like an SSH host key: the primary prints a code and its fingerprint, and the other node reports the fingerprint of whoever answered before the code is ever sent.

kydns replica invite                  # on the primary: a code and a fingerprint
kydns replica join 192.168.1.10:8443 <code> --fingerprint <fingerprint>
kydns replica list                    # on the primary: lag and last sync per replica
kydns replica status                  # on a replica: what it follows, and why a poll failed

Compare the two fingerprints yourself. Without --fingerprint, join shows you the one it was presented and waits for a yes; if it does not match, say no. A mismatch means something is answering in the primary's place, and the code is not sent, so it stays good.

Restart the replica once more after joining — the pull loop is built at startup. From then on it polls every five seconds, and a change on the primary is answered by the replica a few seconds later. Writes on the replica are refused with the address of the primary to make them on: in the API, in the CLI, and in the UI, whose editing controls are disabled with the same reason.

The Replication screen in the web UI does all of this too: minting an invite, listing replicas with their lag, removing one, and promoting.

When the primary is down

The replica keeps answering every name it last pulled, for as long as it takes. It shows how long since its last sync, and puts up a standing banner once that passes a minute. Service health reads as unknown, not as the last thing it heard: the node that runs the checks is the one that is missing.

Promote it once you have decided the primary is not coming back:

kydns replica promote

It accepts writes immediately. Two things are then yours to do:

  • Set replication.listen on it and restart, or nothing can follow it. Promotion opens no listener and does not edit your config file.
  • Do not switch the old primary back on until it has been demoted or rebuilt. Demote it with kydns replica join against the new primary, which discards its configuration on the first pull. Two primaries serving the same replicas is the one state KyDNS can neither detect nor undo.

Demoting an old primary means editing both keys in its config: set replication.primary to the new primary, and remove the replication.listen it still has from when it was one. A node with both keys refuses to start, so setting only the first leaves that box with no DNS at all.

The promotion is recorded in the database, so a promoted node comes back a primary at every later start even though replication.primary is still in its file. It logs that the key is being ignored; remove it when convenient.

There is no automatic failover, no leader election, and no peer discovery. Promotion is a decision you make, and one primary is the only supported shape.

Running it

kydns serve --config /etc/kydns/kydns.yaml

On first start with no admin account, KyDNS writes a setup token to <data_dir>/setup-token and logs it. Open the admin listener, enter that token, and choose a password. A bootstrap API token for the CLI is written to <data_dir>/bootstrap-token.

The CLI reads KYDNS_URL (default http://127.0.0.1:8053) and KYDNS_TOKEN. Everything except serve and admin goes through the JSON API, so the CLI works from any machine that can reach the admin listener:

Command Does
kydns serve Run the DNS and admin servers.
kydns service add|list|rm Manage services, their addresses and aliases.
kydns record add|list|rm Manage manual A, AAAA and CNAME records.
kydns view add|list|rm Manage per-subnet views.
kydns token add|list|rm Manage API tokens.
kydns settings get Print the settings the server is running.
kydns settings set k=v ... Change them. --confirm-public <cidr> for a public allow_query range.
kydns replica invite|list|remove Manage the replicas this node serves. invite prints a pairing code and this node's fingerprint; confirm the fingerprint on the replica before entering the code.
kydns export / kydns import Read or write the registry and settings as YAML or JSON.
kydns admin reset-password Local recovery. Opens the database directly.

Forgot the admin password

kydns admin reset-password --config /etc/kydns/kydns.yaml

This is the one command that opens the database directly instead of going through the API, because there is otherwise no way back in. It needs write access to the database file — anyone who has that can already read every hash and token in it, so it is not an extra way in. Restart KyDNS afterwards to end any sessions that are still signed in.

In Docker, where the image has no shell:

docker compose run --rm -it kydns admin reset-password

Docker

KyDNS joins a LAN-attached Docker network your host already owns, so the container gets its own IP address on your LAN, binds port 53 there, and never contends with systemd-resolved or dnsmasq on the host. unRAID calls that network br0. If AdGuard Home or Pi-hole already has a LAN address, KyDNS joins the same network they are on.

Compose joins the network. It never creates or deletes it — a network handing out addresses on your physical LAN outlives any one stack.

cp .env.example .env                    # set KYDNS_IP, and KYDNS_NETWORK if not br0
cp kydns.example.yaml kydns.yaml
make setup                              # detects your interface, subnet, gateway
docker compose up -d
docker compose logs kydns | grep setup-token

make setup reads the host's default route and appends the interface, subnet and gateway to .env. It only adds keys that are missing, so re-running it is safe and it never overwrites a value you set by hand. Compose creates the network itself on up — there is no separate network step.

Already have the network — unRAID's br0, or whatever AdGuard Home and Pi-hole are on? Set KYDNS_NETWORK to its name. Compose adopts it, prints a warning saying it did not create it, and leaves it in place on docker compose down. Only a network compose created itself gets removed.

In kydns.yaml change two of the three file-owned settings from their defaults: data_dir: /var/lib/kydns, and admin.listen: "0.0.0.0:8053". Leaving admin on 127.0.0.1 would bind the container's loopback, reachable from nowhere. dns.listen is already right at :53.

The image already has both set, in kydns.docker.yaml baked in at /etc/kydns/kydns.yaml, so a docker run with no config mounted at all starts and serves. The compose file mounts your kydns.yaml over it, which is what makes the two settings above yours to get right. Nothing else needs to be in that file: every other setting seeds the database on the first run and is edited under Settings from then on.

Pick a KYDNS_IP inside that network's subnet, outside the router's DHCP pool, and different from any address AdGuard Home or Pi-hole already holds. Two DNS servers can each own port 53 at once when each has its own LAN address, which is how you move one client at a time instead of the whole house.

Unraid

On Unraid there is no YAML to edit at all. All three file-owned keys are the container template's job: one volume mapping for data_dir, and two port mappings for dns.listen and admin.listen. The baked-in kydns.docker.yaml already sets the two that need it, and the template's mappings decide where they land on the host. Set the container on your br0 network so it gets its own LAN address, start it, read the setup token from the log, and do everything else — upstreams, the private domain, filtering, allow_query — from the web UI.

Pre-creating it

Compose creates the network for you. Pre-create it only if you want it shared with other containers or kept across compose down — compose adopts an existing network rather than complaining:

docker network create -d ipvlan \
  -o parent=eth0 \
  --subnet 192.168.1.0/24 --gateway 192.168.1.1 \
  --ip-range 192.168.1.53/32 \
  br0

The /32 range is deliberate. Give Docker the whole subnet and it will eventually allocate an address your router later leases to a phone, which presents as DNS failing at unpredictable hours.

ipvlan shares the host's MAC and only adds an address, so switch port security and DHCP snooping have nothing to object to, and it is the driver that survives WiFi. macvlan gives each container its own MAC, which matters only if you want the router to hold a DHCP reservation for it — KyDNS uses a static address, so it does not. Set KYDNS_NET_DRIVER=macvlan if you want it anyway.

Three things that bite

  • The host cannot reach the container. macvlan and ipvlan both deliberately block host-to-container traffic. Everything else on the LAN is fine; only the Docker host itself can't reach KyDNS. That matters if the host resolves through it. docker-compose.yml has the shim-interface fix.
  • Use the named volume for data_dir, not a host directory. The container drops all capabilities except NET_BIND_SERVICE, losing CAP_DAC_OVERRIDE, so it cannot write into a directory owned by another user. A bind mount from your home directory fails with unable to open database file. Chown it to root if you need one.
  • The image has no shell. It is distroless, about 29 MB. Read the first-run tokens from the volume rather than with docker exec:
docker run --rm -v kydns-server_kydns-data:/d busybox cat /d/setup-token

WiFi

ipvlan works over WiFi, which is why it is the default. Some access points with client isolation still interfere. Note that there is no br0 over WiFi in the literal sense — a station-mode interface cannot be enslaved to a Linux bridge — but nothing here needs one: the network just has to exist under that name, and an ipvlan network with parent=wlan0 does.

Security model

  • DNS data stays on the operator's network.
  • The server does not send query history to a KySecurity service.
  • Administrative changes require authentication and are recorded locally.
  • Configuration exports must not contain upstream credentials or private keys.
  • Public DNS names and private service names remain separate by default.

See DESGINE.md for the architecture, LOGGING.md for logging requirements, and SECURITY.md for security policy.

Scope

KyDNS is a small DNS server, a service registry, a web UI, and opt-out DNS blackhole filtering. It does not provide parental controls, device posture, or traffic inspection.

Repository status

v1 is implemented, and a single node is still the normal deployment. See the design spec for the architecture and the reasoning behind each decision, and the replication spec for the second node.

make test    # every package
make build   # bin/kydns

CI runs gofmt, go vet, go mod tidy, the race-enabled test suite, a cgo-free build, and a Docker job that resolves both a local service name and a public name over the default DoT upstream.

License

KyDNS is licensed under the GNU AGPL v3. The web UI carries the notice and serves the full license text from its About popover, so an operator running a modified copy over a network can always see what they are running.

apps.marketingCta.appInstallTitle

apps.marketingCta.appInstallDescription

apps.installHelp.stepOpen apps.installHelp.stepSearchApp apps.installHelp.stepReview apps.installHelp.stepInstall

apps.detail.sections.requirements

A br0 (or other LAN-attached) custom Docker network, and a free static IP on your LAN outside the router's DHCP pool.

apps.detail.sections.categories

apps.detail.sections.details

apps.detail.details.repository
ghcr.io/yoshiofthewire/kydns-server:latest
apps.detail.details.lastUpdated2026-08-13
apps.detail.details.firstSeen2026-08-13

apps.detail.sections.runtime

apps.detail.details.webui
http://[IP]:[PORT:8053]/
apps.detail.details.network
br0
apps.detail.details.shell
sh
apps.detail.details.privileged
false
apps.detail.details.extraParams
--cap-drop ALL --cap-add NET_BIND_SERVICE --cap-add DAC_OVERRIDE --security-opt no-new-privileges:true

apps.detail.sections.configuration

Config FilePathro

Optional. The image ships a working config, so leave this BLANK unless you want to change a setting that is not in this template — upstream resolvers, the private domain, reverse zones, per-query logging. If you do use it: copy kydns.example.yaml from the project to somewhere like /mnt/user/appdata/kydns/kydns.yaml, edit it, and put that host path here. The file must already exist — Docker creates a directory when the host path is missing, and the container will then refuse to start. Keep data_dir: /var/lib/kydns and admin.listen: &quot;0.0.0.0:8053&quot; in whatever you write. Services, records, views, and API tokens are not in this file; they live in the database and are edited in the web UI.

apps.detail.config.target
/etc/kydns/kydns.yaml
Data DirectoryPathrw

Holds kydns.db plus the first-run setup and bootstrap tokens. BACK THIS UP — it is every service, record, view, user, and API token you have. Must match data_dir in the config file above. The container writes here as root, and Unraid creates appdata directories owned by nobody:users, which is why this template grants DAC_OVERRIDE alongside dropping every other capability.

apps.detail.config.target
/var/lib/kydns
apps.detail.config.default
/mnt/user/appdata/kydns/data
apps.detail.config.value
/mnt/user/appdata/kydns/data
DNS Port (UDP)Portudp

The DNS port. On a custom br0 network this is the port on KyDNS's own LAN address, so it does not contend with anything on the Unraid host. Point your clients or your router's DHCP at that address.

apps.detail.config.target
53
apps.detail.config.default
53
apps.detail.config.value
53
DNS Port (TCP)Porttcp

DNS over TCP, used for truncated or large answers. Bound alongside the UDP port; keep them the same.

apps.detail.config.target
53
apps.detail.config.default
53
apps.detail.config.value
53
WebUI PortPorttcp

The web UI and JSON API. Must match admin.listen in the config file, which needs to be 0.0.0.0:8053 here rather than the 127.0.0.1 default. Plain HTTP — put a TLS-terminating reverse proxy in front if you expose it beyond your LAN.

apps.detail.config.target
8053
apps.detail.config.default
8053
apps.detail.config.value
8053
DHCP Lease FilePathro

Optional: a dnsmasq lease file for DHCP lease discovery, so leased hosts resolve and can be promoted to services. Also set discovery.dhcp_lease_file to this container path in the config file — the mapping alone does not tell KyDNS where to look. Leave blank if your DHCP server is your router.

apps.detail.config.target
/var/lib/misc/dnsmasq.leases