serverless-dns: a self-hosted, Pi-Hole style DNS resolver for edge platforms
The RethinkDNS resolver that deploys to Cloudflare Workers, Deno Deploy, Fastly, and Fly.io
At a glance
- What is it?
- serverless-dns runs a content-blocking DoH and DoT stub resolver on Cloudflare Workers, Deno Deploy, Fastly Compute@Edge and Fly.io. It is a good fit if you want a filtering resolver without running a server, and a poor fit if you need a full recursive resolver or a mature test suite.
- Who is it for?
- Adopt serverless-dns if you want a filtering DoH/DoT endpoint on an edge platform's free tier and you are comfortable editing src/core/env.js or wrangler.toml instead of a web UI. Do not adopt it if you need a recursive resolver, a large supported configuration surface, or a tested codebase: package.json still runs a placeholder test script.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 147 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What serverless-dns actually replaces
A Pi-Hole deployment means a machine that stays up, a blocklist that has to be refreshed, and a DNS port exposed to your network. serverless-dns takes the same idea and moves the resolver into an edge runtime. The README describes it as a "Pi-Hole esque content-blocking, serverless, stub DNS-over-HTTPS (DoH) and DNS-over-TLS (DoT) resolver" that runs out-of-the-box on Cloudflare Workers, Deno Deploy, Fastly Compute@Edge and Fly.io.
The audience is narrow and specific. It is for people who already trust an edge provider with their traffic and want a filtering resolver they can point a router or a phone at. The README states that the free tiers of all four platforms should cover 10 to 20 devices worth of DNS traffic per month, which frames the intended scale: a household, a small office, a personal network. It is not aimed at operators who need per-tenant policy, query logging pipelines, or a resolver serving thousands of clients.
The project is a stub resolver, not a recursive one. It forwards queries upstream and applies blocklists on the way back. That distinction matters more than any feature list, because it determines what happens when an upstream is unreachable and why there is no root zone logic anywhere in the request path.
The request path from client to blocklist decision
The README lays out the flow in two lines. First, client to `src/server-[node|workers|deno]`, then to `doh.js`, then to `plugin.js`. Second, inside `plugin.js`: `user-op.js` to `cache-resolver.js` to `cc.js` to `resolver.js`.
Read that as a pipeline. The platform-specific server file is the only part that changes between Cloudflare, Deno, Fastly and Fly.io. Everything downstream is shared, which is why one repository can target four runtimes. `doh.js` handles the DoH side of the protocol. `plugin.js` is the orchestration layer, and its four stages suggest a fixed order: user operations first, then a cache lookup, then whatever `cc.js` represents in the chain, then the actual upstream resolution. The naming is terse and the README does not expand each stage, so anyone modifying policy has to read the source rather than a design document.
The dependency list hints at how the hot path is built. `@serverless-dns/dns-parser` handles packet parsing, `@serverless-dns/trie` looks like the structure behind domain matching, and `@serverless-dns/lfu-cache` is a least-frequently-used cache. All three are separate repositories under the same organization, so the resolver is assembled from small pieces rather than one monolith. The README quotes server-side processing of 0 to 2 ms at the median and end-to-end latency of 10 to 30 ms at the median, varying by region and network. Those are the project's own figures, not an independent measurement.
Auth is worth noting because it is unusual. The README says serverless-dns supports an alphanumeric bearer token for both DoH and DoT, generated by appending `hex(hmac-sha256(msg-key|domain.tld), msg)` to the `ACCESS_KEYS` environment variable in CSV format, with `msg` currently fixed to `sdns-public-auth-info`. That is a shared-secret scheme, not an identity system. There is no per-user accounting behind it.
Deploying to Cloudflare Workers and opening /configure
The README calls Cloudflare Workers the easiest platform to set up and offers a Deploy to Cloudflare Workers button. The manual path starts with cloning and installing dependencies on Node v22 or later, which the README recommends installing via nvm.
git clone https://github.com/serverless-dns/serverless-dns.git
cd ./serverless-dns
nvm install --lts
npm iAfter dependencies are installed, the repository ships a `run` script that takes a single-letter platform argument. For Cloudflare Workers through Wrangler, install Wrangler first and then invoke the script.
npm i wrangler --save-dev
./run wThe README notes that Wrangler authentication has to be set up before this step and links to Cloudflare's authentication documentation. The same `run` script covers the other targets: `./run n` for Node, `./run d` for Deno, `./run f` for Fastly Compute@Edge. It also accepts a profiler argument, `./run n [cpu|fn|mem]`, for clinicjs profiling.
Once the worker is deployed, blocklists are configured from the browser. The README says to visit `https://<my-domain>.tld/configure`, which should load a page similar to RethinkDNS' own configure page. That is the whole configuration story: no dashboard, no config file to upload, just the deployed endpoint serving its own settings UI. Environment variables are handled differently per platform. The README points at `src/core/env.js` for defaults, `wrangler.toml` for Cloudflare Workers, and `fastly.toml` for Fastly Compute@Edge. If you deploy to Cloudflare and the defaults do not fit, `wrangler.toml` is where you edit, not `env.js`.
Where serverless-dns is the wrong tool
The most concrete limitation is that there is no test suite. The `test` script in package.json is `echo "Error: no test specified" && exit 1`. A repository with a `test/` directory at the top level but a failing placeholder script means you cannot verify a change by running the project's own tests. For a component that sits in front of every DNS query on your network, that is a real risk, and it is the first thing to weigh against the convenience of one-click deployment.
Second, the platform matrix is not uniform. Cloudflare and Fastly are listed as Easy, Deno as Moderate, Fly.io as Hard. The runtimes differ too: Cloudflare and Deno use isolates, Fastly uses Fastly JS compiled to Wasm via `js-compute-runtime`, and Fly.io runs Node in a MicroVM. The shared code is shared, but the deployment and debugging experience is not. Choosing Fly.io because you prefer Node means accepting the hardest setup path in the table.
Third, this is a stub resolver. If a query is not cached and the upstream path fails, there is no fallback described in the README. There is also no mention of query logging, retention controls, or an admin API. Anyone who needs an audit trail of blocked domains will not find it here. And the authentication model is a static bearer token derived from a shared `msg-key`, which is adequate for a household and inadequate for anything where clients must be individually revocable.
serverless-dns versus running Pi-Hole or AdGuard Home
The closest comparison is Pi-Hole itself, or AdGuard Home, both of which run as a service on a machine you control. The difference is not the blocklist, it is where the process lives. Pi-Hole gives you a local database of query history, a web dashboard, per-client groups, and DHCP integration. serverless-dns gives you none of that. In exchange, there is no host to patch, no port to forward, and no single point of failure in your house. If your home server goes down, every device loses DNS. If a Cloudflare Worker is redeployed, the resolver is back in seconds.
That trade is the whole decision. serverless-dns is also not competing with a public resolver such as 1.1.1.1 on speed. The README's own numbers put end-to-end latency at 10 to 30 ms median, which is the normal range for a DoH resolver reached over the public internet. What you get over a public resolver is the blocklist and the ability to point `sky.rethinkdns.com`-style endpoints at your own domain.
A second alternative is writing your own worker. The request flow here is short: a server file, `doh.js`, and a four-stage plugin chain. If your filtering rules are simple, a few hundred lines on Cloudflare Workers may be easier to reason about than adopting a project whose internals are documented mostly by file names. The counterargument is the dependencies: `dns-parser`, `trie` and `lfu-cache` are non-trivial to reimplement correctly, and the trie-based matching is exactly the part you would get wrong first.
Maintenance, licensing and what upgrading costs
The repository is not archived, and the last push was on 2026-05-06. Releases are infrequent and irregular: v0.1.31 (Palghat) on 2025-10-29, v0.1.30 (Nairobi) on 2024-10-17, and v0.1.25 (Damishq) on 2024-09-29. Roughly a year separates the last two releases, so treating this as a project with a steady release cadence would be a mistake. The package.json version is 2.0.0 while the release tags are 0.1.x, which is a discrepancy worth understanding before you pin anything.
Upgrade cost depends on which platform you chose. On Cloudflare, a redeploy through Wrangler picks up the new code, and `wrangler.toml` carries your environment variables. On Fastly, the build chain runs webpack and then `js-compute-runtime` to produce a Wasm module, so an upgrade means rebuilding the artifact. On Fly.io you are on the Hard path with a Node MicroVM, and the Dockerfiles in the repository root (`node.Dockerfile`, `bun.Dockerfile`, `deno.Dockerfile`) indicate several build variants to keep straight. Dependencies are pinned to GitHub references rather than registry versions, for example `@serverless-dns/dns-parser` at `#v2.1.2` and `@serverless-dns/trie` at `#v0.0.17`, so `npm update` behaves differently than it would with semver ranges.
The licence is MPL-2.0, stated in both the LICENSE file and package.json. MPL-2.0 is file-level copyleft: modifications to covered files must be made available under the same licence, while larger works that combine it with other code can be licensed differently. If you fork and modify `doh.js` or `plugin.js` and distribute the result, those files carry obligations. Running a modified copy as a private resolver is a different situation. This is a description of the licence text, not legal advice; check with counsel if you plan to redistribute.
Editorial conclusion
Adopt serverless-dns if you want a filtering DoH/DoT endpoint on an edge platform's free tier and you are comfortable editing src/core/env.js or wrangler.toml instead of a web UI. Do not adopt it if you need a recursive resolver, a large supported configuration surface, or a tested codebase: package.json still runs a placeholder test script. Before committing, verify that your chosen platform's free tier covers your DNS query volume, that the /configure page loads on your deployment, and that you can reach the upstream resolvers you intend to use.
Frequently asked questions
What is serverless-dns and who is it for?
It is a self-hosted, Pi-Hole style content-blocking stub DNS-over-HTTPS and DNS-over-TLS resolver that runs on Cloudflare Workers, Deno Deploy, Fastly Compute@Edge and Fly.io. It suits individuals or small networks that want a filtering DoH or DoT endpoint without maintaining a server.
How do I install serverless-dns on Cloudflare Workers?
The README recommends Cloudflare as the easiest platform and offers a Deploy to Cloudflare Workers button. Manually, you clone the repository, run nvm install --lts and npm i, then install Wrangler with npm i wrangler --save-dev and run ./run w after setting up Wrangler authentication.
How do I set up blocklists in serverless-dns?
The README says to visit https://<my-domain>.tld/configure from your browser after deployment, which should load a page similar to RethinkDNS' configure page. Blocklist selection happens there rather than in a config file.
Does serverless-dns support DNS-over-TLS?
Yes. The README describes it as a stub DNS-over-HTTPS and DNS-over-TLS resolver, and the RethinkDNS production table lists Fly.io as serving both DoH and DoT at max.rethinkdns.com. The Cloudflare, Deno and Fastly endpoints listed there are DoH.
How does authentication work in serverless-dns?
The README states it supports an alphanumeric bearer token for both DoH and DoT. You append hex(hmac-sha256(msg-key|domain.tld), msg) to the ACCESS_KEYS environment variable in CSV format, with msg currently fixed to sdns-public-auth-info.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/serverless-dns-serverless-dns)