-
v0.0.25
StableGates / container (push) Successful in 10sCI / gates (push) Successful in 23m56sRelease / guard (push) Successful in 1m47sGates / frontend (push) Successful in 2m14sGates / test (push) Successful in 2m40sGates / test-aarch64 (push) Successful in 5m39sGates / package (push) Successful in 47sGates / container (push) Successful in 11sRelease / gates (push) Successful in 9m20sGates / frontend (push) Successful in 2m12sRelease / publish (push) Successful in 10m46sGates / test (push) Successful in 2m32sGates / test-aarch64 (push) Successful in 6m35sGates / package (push) Successful in 4m31sreleased this
2026-10-22 19:34:55 +00:00 | 0 commits to master since this releaseFixed
- The DNS cache's memory is bounded in bytes, not only in entries. Each cached answer is copied at whatever size the upstream sent, so 10000 entries could in principle hold 10000 large answers. The answers now share a budget of 256 bytes per
cache.sizeentry, about 2.4 MiB at the default, and when a new answer does not fit, answers nobody has asked for lately make room for it. An answer larger than the whole budget is not cached./metricsreportsnxdns_cache_response_bytesagainstnxdns_cache_max_bytes, and countsnxdns_cache_oversized_totalandnxdns_cache_copy_failures_total. - The DNS cache no longer evicts an answer while it has free room. A new answer replaced an older one whenever the eviction scan met it first, even with most of the cache empty. A copy of an answer that failed for lack of memory was also dropped without a trace; it is now counted, and a failed replacement keeps the answer already cached.
- A refused query history says what to do. When nxdns refuses to open
querylog.db, because a newer release wrote it or it cannot be converted, the message now gives the remedy: run the newer release again, put back thequerylog.db.pre-migrate-*copy, or move the file and its-waland-shmfiles aside to start an empty history. It used to point at a documentation page. - A refused start no longer gives advice that does not apply. After a query history refusal,
nxdns runalso printed "runnxdns check" and "load one withnxdns import <file>", though the configuration was fine. The import hint now appears only when the database has no usable upstream, and neither hint follows a database refusal.
Changed
- Blocklists take less memory, and a refresh peaks lower. A list that names a domain both on its own and with its subdomains, as Adblock-style lists do for every entry, now stores that name once instead of twice. A reload reads one list's compiled files at a time instead of all of them at once, and the startup pass no longer rebuilds the blocklists when no list was due for a refresh.
- The query-history buffer defaults to 1024 queries instead of 10000, under 2 MiB instead of about 17. It only holds queries that arrive while a write is in progress.
- A live query stream takes memory only while someone watches it. Each of the 32 possible streams reserved its buffer at startup, about 3.6 MiB in all. A stream that cannot get its buffer is refused with its own message and counted in
nxdns_live_stream_allocation_failures_total. - All DNS listeners share one budget of 64 queries at a time, about 4.6 MiB, where each UDP and TCP listener used to reserve room for 64 of its own, about 27 MiB across the four. The Pi's busiest moment over a day was 27 queries at once. A query beyond the budget waits for a free one instead of failing: over UDP it waits in the system's socket buffer, over TCP, DoT and DoH for at most the connection's idle time.
/metricscounts the waits asnxdns_dns_query_permit_waits_total, their time asnxdns_dns_query_permit_wait_seconds_totaland stream waits that ran out asnxdns_dns_query_permit_wait_timeouts_total, showsnxdns_dns_query_permits_waiting, and reports what the system holds and drops for the UDP/53 sockets asnxdns_udp_server_receive_queue_bytesandnxdns_udp_server_kernel_drops_total. TCP, DoT and DoH each accept 16 connections at a time instead of 64, and the DoT and DoH listeners now reportrejected_at_capacity_totaltoo. - A query waiting for its turn at an upstream holds one thread instead of three, so a burst grows the server's thread pool less, and transparent huge pages are switched off for the process, so a thread's stack no longer holds 2 MiB on systems that enable them for everything. Work that could not get a thread used to fail as a generic error or not be counted at all;
/metricsnow counts it asnxdns_concurrency_unavailable_total, by where it happened. - Each database connection keeps a page cache sized for its job instead of SQLite's 2 MiB default on all seven: 256 KiB for the configuration database and for retention, 1 MiB for the query-history writer and reader.
- The documentation is written for people running nxdns. It starts from Get started, which downloads a release instead of building from source, and drops the API reference, the troubleshooting page and the performance pages. It is published at https://nxdns.sh/docs/, and
INSTALL.mdin the tarball links there.
Removed
nxdns_udp_server_dropped_no_slot_total, because a UDP query is no longer dropped for lack of room: it waits. A query still waiting when the listener shuts down is counted in the newnxdns_udp_server_dropped_at_shutdown_total.
Artifacts
b49d3f980d198627344aa621c1aacd28b94ad2ce5005013337fe764141b162fd nxdns-0.0.25-x86_64-linux-musl.tar.gz ccada2e757115fee6afbdac845abf89ba36155924963083d3898f45acb4a77f3 nxdns-0.0.25-aarch64-linux-musl.tar.gz 8cc3f364a797022e86e78e3877de851f8110cd087244790026bddbd806d1e6b6 IMAGE-DIGEST.txtgit.mial.net/mokhtar/nxdns:0.0.25@sha256:c225e4604ad3edaade1346060679659ed081ccd3cfee50e93b5b1026d2d60035Commits since v0.0.24
27e1e49 platform: the huge pages test skips where the kernel or emulator has no such switch 98283e1 ci: install the pinned node from its tarball instead of trusting the runner image f834418 build: bump version to 0.0.25 90b022f changelog: date the 0.0.25 section 91ed5ae server: a query that finds every permit taken waits for one instead of failing, the budget is 64, permit and upstream turns wait on a futex with one thread each, the thread pool has no task limit, and transparent huge pages are off for the process 63cc5f8 server: every dns listener shares one budget of 32 queries, stream listeners take 16 connections, the task pool is capped from those bounds, and work it cannot start is counted by site e4e405f cache: a byte budget of 256 bytes per entry bounds the cached answers, evicting by clock, and a full or failed copy is counted instead of swallowed d951d03 querylog: the writer queue defaults to 1024 entries, and a live stream allocates its ring on subscribe instead of at boot 0cafd77 filter: a reload builds the snapshot one source at a time, and the startup pass reloads only when a source changed or was not enforced c2eac8d storage: each runtime database connection keeps a page cache sized for its role, read back at open 8f27945 ci: time limits on the test, test-aarch64, package and container jobs 5a51e2d tests: a failed server check fails the test instead of hanging it, and the tls idle tests leave the handshake room 4916b01 filter: store each blocklist name once, flagged exact and subdomains, instead of in two sets per source 2a06239 ci: use the runner image's node instead of setup-node, which downloaded it again on every run 7e06642 ci: build nxdns.sh and deploy it as a cloudflare worker with static assets 93e2683 website: nxdns.sh on astro and starlight, with the docs and 0.0.24 screenshots c21e5a9 docs: rewrite for people running nxdns; query-history refusals name their remedy and a refused start gives no wrong hints fbc8fd7 specs: memory budget drops the removed performance page and keeps stack_size, a thp artifact explained the 2 mib stacksDownloads
- The DNS cache's memory is bounded in bytes, not only in entries. Each cached answer is copied at whatever size the upstream sent, so 10000 entries could in principle hold 10000 large answers. The answers now share a budget of 256 bytes per