Self-hosted service
DNSCrypt/encrypted-dns-server avatar
DNSCrypt/encrypted-dns-server

encrypted-dns-server: one port serving DNSCrypt, DoH and QUIC

An easy to install, high-performance, zero maintenance proxy to run an encrypted DNS server.

1,329 stars133 forksRustMIT

At a glance

What is it?
A Rust proxy that terminates DNSCrypt v2 including the post-quantum variant, Anonymized DNSCrypt, DNS-over-HTTPS and QUIC on a single port, with automatic protocol detection, a built-in cache, filtering and Prometheus metrics. The setup depends on a separate recursive resolver, and the DoH path needs a separate certificate-managed proxy.
Who is it for?
encrypted-dns-server fits a self-hosting operator who wants to run their own encrypted DNS endpoint with a single binary and one port, and who is prepared to run a recursive resolver alongside it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the proxy is and what it is not

encrypted-dns-server is described as an easy to install, high-performance, zero maintenance proxy to run an encrypted DNS server. The primary language is Rust, the licence is MIT, and the author in the package metadata is Frank Denis.

The word proxy in the description is load-bearing. This is not a recursive resolver. The README is explicit that the proxy requires a recursive DNS resolver such as Knot, PowerDNS or Unbound, and that the resolver can run locally and only respond to `127.0.0.1`.

External resolvers such as Quad9 or Cloudflare DNS can also be used, and the README attaches a caveat to that option: it may be less reliable due to rate limits. So the deployment has two shapes. Either you run your own resolver and the proxy is the only thing exposed to the network, or you forward to a public resolver and accept that a busy public resolver can rate limit you.

The repository is not archived and the last push was on 2026-10-19. The release history shows 0.9.22 from 2026-09-07, 0.9.21 from 2026-06-20 and 0.9.20 from 2026-05-03, so the version is still below 1.0 and shipping every few months.

One provenance detail: the `Cargo.toml` lists the homepage and repository as `https://github.com/jedisct1/encrypted-dns-server`, while the repository itself lives under the `DNSCrypt` organisation. The crate is published as `encrypted-dns`.

The top level is small and says what the project is: `src/`, an `example-encrypted-dns.toml`, an `undelegated.txt` file, an `AI-POLICY.md`, a `dashboard.png` and a `logo.png`.

Four protocols on one port, detected automatically

The proxy supports DNSCrypt v2 including post-quantum DNSCrypt, Anonymized DNSCrypt, DNS-over-HTTPS forwarding, and QUIC forwarding to something like an HTTP/3 server, sharing the UDP port with DNSCrypt.

The property that makes this useful is stated next: all of these can be served simultaneously, on the same port, usually port 443, and the proxy automatically detects what protocol each client is using.

That last part removes the usual operational headache of an encrypted DNS server. A conventional setup needs a separate TCP port for DNSCrypt, a separate TCP port for DoH, a certificate for the DoH hostname and a separate UDP port for QUIC, and the client has to be told which to use. Here a client that connects to the same port with a DNSCrypt stamp, a DoH bootstrap request or a QUIC handshake gets the right treatment, because the first bytes reveal the protocol.

The post-quantum DNSCrypt support is the newest and most notable item on that list, and it is a real design decision rather than a feature checkbox. The README links the protocol draft describing it, so the mechanism is documented upstream rather than invented here.

Anonymized DNSCrypt is the other protocol worth understanding. Enabling it allows the server to be used as an encrypted DNS relay, which is a different privacy property from simply encrypting your queries to a resolver you trust. It is the closest thing in the project to an anonymity guarantee, and it carries the usual caveats that apply to any relay.

The QUIC forwarding option is for a client that speaks HTTP/3, and it shares the UDP port with DNSCrypt, which is why the same port can serve both.

Installing: a precompiled binary, cargo, or Docker

There are three routes, and the first is deliberately the easiest.

The precompiled x86_64 binary is available as Debian packages and as archives for Linux and Windows, downloaded from the releases page. The README says nothing else has to be installed and that the server does not require any external dependencies. In the Debian package, the example configuration file is at `/usr/share/doc/encrypted-dns/`.

Compiling from source requires rust and a C compiler, with clang recommended. The Rust install is the standard rustup script:

sh
curl -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain nightly
source $HOME/.cargo/env

Note the nightly toolchain flag. That is a signal about the build, and a reader who wants a reproducible build should know about it.

The build and install are then:

sh
cargo install encrypted-dns
strip ~/.cargo/bin/encrypted-dns

The `strip` step is in the README's own instructions rather than being an optional extra, and the executable ends up at `~/.cargo/bin/encrypted-dns` by default.

The third option is Docker, and the README points at a separate repository, `dnscrypt-server-docker`, describing it as the most popular way to deploy an encrypted DNS server. That image bundles a caching DNS resolver, the encrypted DNS proxy and scripts that configure everything, which makes it the option for someone who wants the whole stack rather than just the proxy.

The Rust dependency list explains the runtime shape. It uses tokio for the async runtime, hyper for the HTTP side, libsodium-sys-stable for the cryptography, mimalloc as the allocator, sieve-cache for the cache, dnsstamps for the stamp format, daemonize-simple for backgrounding, and clap for the command line. The presence of `coarsetime` and `rlimit` shows attention to the operational details of a long-running network daemon.

Configuration, the provider key and the state file

The setup is short. The proxy needs a recursive resolver, and for DoH support in addition to DNSCrypt it needs a DoH proxy as well, with `rust-doh` recommended. The README says DoH support is optional because it is currently much more complicated to set up than DNSCrypt, due to certificate management.

The configuration starts by making a copy of the `example-encrypted-dns.toml` configuration file named `encrypted-dns.toml`.

All the parameters live in that file, including the IP addresses to listen on. The README says you should probably at least change the `listen_addrs` and `provider_name` settings.

Then you start the proxy, and it will automatically create a new provider key pair if there isn't one. The DNS stamps are printed on startup and they can be used directly with `dnscrypt-proxy` on the client side. The README's summary of the remaining work is that there is nothing else to do, because certificates are automatically generated and rotated.

That last point is the operational difference between this proxy and a hand-rolled DoH setup. Certificate issuance and rotation are the part that rots, and here it is not your problem.

The state file needs care. The proxy creates and updates a file named `encrypted-dns.state`, which contains the provider secret key along with certificates and encryption keys. The README says not to delete it unless you want to change parameters such as the provider name, and to keep it secret or the keys will be lost, and suggests putting it in a directory only readable by the super-user.

Migrating from `dnscrypt-wrapper` is handled. Check that the provider name in `encrypted-dns.toml` matches the one you previously configured, and that it can be recovered from its DNS stamp if you forgot it, then run:

sh
encrypted-dns --import-from-dnscrypt-wrapper secret.key

The `secret.key` file is the one holding the old dnscrypt-wrapper provider secret key.

The cache serves stale answers when upstream stalls

The proxy includes a key cache as well as a DNS cache, both there to significantly reduce the load on upstream servers.

The DNS cache has a behaviour worth knowing about. If a server is slow or unresponsive, expired cached queries are returned, ensuring that popular domain names always keep being served.

That is a deliberate availability trade. Serving an expired answer is a violation of the DNS TTL contract, and clients that care about freshness will see stale records. In exchange, a resolver outage at the upstream does not translate into a total loss of name resolution for your users, which for a self-hosted service is often the right way round.

The key cache serves a different purpose. It caches the DNSCrypt provider keys, which are long-lived, so the expensive part of the protocol, the key exchange, does not have to be repeated for every client.

Prometheus metrics can optionally be enabled to monitor performance and cache efficiency among other things. The project also ships a `dashboard.png` in the repository root.

The dependency list includes `sieve-cache` for the cache, and `siphasher` and `rustc-hash`, both of which are choices about not letting a hash table become a denial of service vector.

Filtering works only over DNSCrypt, and access control needs a client setting

Two features are documented with conditions attached, and both conditions are the kind of thing that surprises people in production.

Filtering is configured in the `[filtering]` section of the configuration file, and the note is that filtering only works with the DNSCrypt protocol and does not apply to DNS-over-HTTPS forwarding. Since the proxy serves DoH on the same port, a client that happens to use DoH gets unfiltered resolution. For an operator who has published a blocklist, that is a hole rather than a detail.

Access control is enabled in the `[access_control]` section, and it is configured with the `query_meta` configuration value of `dnscrypt-proxy`. So the server side and the client side have to agree, and the server cannot enforce access control on its own.

The anonymized mode, described as allowing the server to be used as an encrypted DNS relay, sits alongside these. A relay is a different trust model from a resolver, and the repository root also contains an `undelegated.txt` file. The repository also carries an `AI-POLICY.md` at the top level, alongside the `undelegated.txt` file, which the README does not describe in detail.

For operators, the practical sequence is: decide which protocols you will actually serve, because each has different filtering and access control behaviour, then configure the client to match.

Where encrypted-dns-server is the wrong choice

The first wrong case is wanting one process for everything. The proxy needs a recursive resolver alongside it, and DoH needs a second proxy with certificate management. If your goal is a single binary on a single box that answers DNS, this is the wrong project, and a caching resolver with built-in DoH support is the thing you want.

The second is relying on filtering to work for all your clients. Because the same port serves DoH and DNSCrypt and filtering applies only to the latter, a mixed client base means a mixed policy. If you need uniform filtering, you need a proxy that terminates one protocol only.

The third is a client base that cannot use DNSCrypt or DoH. The proxy is an encrypted DNS server, and a client that wants plain DNS over port 53 is not served by it. A local stub resolver on the same host bridges that, and the README's model of a local resolver answering only on `127.0.0.1` is exactly that arrangement.

The fourth is a hard requirement for reproducible builds from source. The documented Rust install uses the nightly toolchain, and nightly means the compiler can change between your build and someone else's.

What the project is genuinely good at is the part that is hard to do well: a single port, automatic protocol detection, automatic certificate management, a relay mode, and a state file that makes a provider key stable across restarts. For an operator running their own encrypted endpoint for a community or a small organisation, that combination is the reason to use this rather than a general purpose DNS server.

Editorial conclusion

encrypted-dns-server fits a self-hosting operator who wants to run their own encrypted DNS endpoint with a single binary and one port, and who is prepared to run a recursive resolver alongside it. It does not fit an operator who wants one process for everything, since the recursive resolver and, for DoH, a separate proxy with certificate management are additional moving parts, and it does not fit a deployment that expects filtering to apply to DoH clients, because the README says filtering works only with DNSCrypt. Before deploying it, copy `example-encrypted-dns.toml` to `encrypted-dns.toml` and set at least `listen_addrs` and `provider_name`, protect the `encrypted-dns.state` file because it holds the provider secret key, and decide on a certificate strategy before promising DoH to users.

Frequently asked questions

What does encrypted-dns-server need to run?

It needs a recursive DNS resolver such as Knot, PowerDNS or Unbound, which can run locally and respond only to `127.0.0.1`. External resolvers like Quad9 or Cloudflare DNS can be used instead, but the README warns that this may be less reliable due to rate limits.

Which protocols can encrypted-dns-server serve on one port?

DNSCrypt v2 including the post-quantum variant, Anonymized DNSCrypt, DNS-over-HTTPS forwarding and QUIC forwarding. All can be served simultaneously on the same port, usually 443, with the proxy detecting the protocol per client automatically.

How do I configure encrypted-dns-server?

Copy `example-encrypted-dns.toml` to `encrypted-dns.toml` and review it. All the parameters are in that file, including the listen addresses, and the README says you should probably at least change the `listen_addrs` and `provider_name` settings.

Why must I keep the encrypted-dns.state file?

It contains the provider secret key along with certificates and encryption keys. The README says not to delete it unless you want to change parameters such as the provider name, and to keep it secret or the keys will be lost, suggesting a directory readable only by the super-user.

Does domain filtering work for DNS-over-HTTPS clients?

No. The README states that filtering is configured in the `[filtering]` section but only works with the DNSCrypt protocol, and does not apply to DNS-over-HTTPS forwarding.

Official sources

  1. DNSCrypt/encrypted-dns-server on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dnscrypt-encrypted-dns-server.svg)](https://hysenlabs.com/projects/dnscrypt-encrypted-dns-server)

Was this useful?