DNSCrypt
3D render: a stack of white and metal server units with status lights, beside a glass query capsule

Linux guide

dnscrypt-proxy on Linux: Packages, systemd and Port 53 Conflicts

The wiki opens its Linux page with a warning that rings true: installing a DNS cache can be one of the fiddliest jobs on a modern distribution, because systemd and network managers already run DNS services of their own. Setting up dnscrypt-proxy on Linux is therefore less about the binary and more about deciding which program owns port 53.

Hop 01

Step zero: who owns port 53

Before installing anything, look at what already listens on the DNS port. The command below prints every socket bound to it together with the owning process. On desktops it typically shows systemd-resolved, sometimes dnsmasq started by NetworkManager, occasionally unbound or an older proxy copy. Only one program can hold a given address and port.

sudo ss -lp 'sport = :domain'

Removable caches such as unbound can simply be uninstalled with apt-get remove or pacman -R. The resolver daemon of systemd cannot be removed, only stopped and disabled, or kept and pointed at the proxy, as described further down. Background on why the port number matters, and which ports encrypted DNS uses upstream, is on the DNS port page.

Hop 02

Packages for Debian, Ubuntu and Arch Linux

Distribution packages exist for the major families. Ubuntu has carried version 2 since 18.10, Debian ships it in testing and unstable, and on Arch Linux it is one pacman command. Packages put the configuration under /etc/dnscrypt-proxy/ and bring systemd units, but they update on the distribution’s schedule, not the project’s.

# Debian testing (use -t unstable for unstable)
sudo apt update && sudo apt install -t testing dnscrypt-proxy
# Ubuntu 18.10 and later
sudo apt install dnscrypt-proxy
# Arch Linux
sudo pacman -S dnscrypt-proxy

On Arch Linux you choose between two mutually exclusive start methods. Socket activation needs listen_addresses = [ ] in the TOML file and is enabled with systemctl enable --now dnscrypt-proxy.socket. The plain service keeps listen_addresses filled and uses systemctl enable --now dnscrypt-proxy.service. Pick one; enabling both produces exactly the port conflict you checked for above.

The Debian and Ubuntu guide adds a reset step that saves a lot of guessing later. Copy the shipped examples from /usr/share/doc/dnscrypt-proxy/examples/ into /etc/dnscrypt-proxy/, overwrite the packaged TOML file with the example, then run dnscrypt-proxy -check from that folder. It should print Configuration successfully checked, and -list shows which resolvers the current filters admit, before any service is touched.

Hop 03

The release tarball and the built-in service

The tarball route works on any distribution with a kernel of 2.6.23 or later. Download linux_x86_64 (or linux_arm64, linux_arm and the MIPS builds for small boards), verify it as described on the installation hub, and extract it to a root-owned folder such as /opt/dnscrypt-proxy so other users cannot modify it.

  1. Get a root shell with sudo -s.
  2. cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml
  3. ./dnscrypt-proxy and read the notices.
  4. In a second terminal: ./dnscrypt-proxy -resolve example.com.
  5. Stop it, then ./dnscrypt-proxy -service install and -service start from the same folder.

The installer writes a standard systemd unit that assumes the executable and the configuration share a folder. It is deliberately boring, and that is the point: upstream supports it, while socket activation is described as not well tested. Upgrading means replacing the executable and restarting the service, which is also the moment to read the release notes.

Hop 04

Living with systemd-resolved and NetworkManager

There are two clean ways to combine the proxy with systemd-resolved, and both appear in the wiki. The generic guide stops and disables it, then replaces /etc/resolv.conf (often a symlink, so delete it first) with a file naming 127.0.0.1. The Debian guide keeps the daemon and sets DNS=127.0.0.1 in the [Resolve] section of /etc/systemd/resolved.conf.

# /etc/systemd/resolved.conf
[Resolve]
DNS=127.0.0.1

Keeping systemd-resolved means restarting it with systemctl restart systemd-resolved after the edit, and nothing else fights over /etc/resolv.conf. Disabling it is simpler to reason about but invites network managers and DHCP clients to rewrite the file later. If that happens, chattr +i /etc/resolv.conf locks it, and chattr -i unlocks it for the next change.

NetworkManager can start its own dnsmasq. Since the proxy caches already, the Debian guide suggests commenting out dns=dnsmasq in /etc/NetworkManager/NetworkManager.conf. If you prefer to keep dnsmasq, move the proxy to 127.0.2.1:53 and give dnsmasq a drop-in with server=127.0.2.1, no-resolv and proxy-dnssec, so it forwards every lookup to the proxy and keeps its own cache out of the way.

Hop 05

Verifying dnscrypt-proxy on Linux

Run dnscrypt-proxy -resolve example.com again once the system DNS points at the proxy. The output names the resolver that answered, the addresses returned and whether the domain is DNSSEC signed. Then browse, update packages and use the machine normally for a while: problems with dnscrypt-proxy on Linux tend to appear when a service restarts, not in the first minute.

An outside check closes the loop. The DNS leak check lists queries that reveal which resolver address reached a public service; it should belong to a server you configured. Repeat it after a reboot and after reconnecting Wi-Fi, since those are the moments when NetworkManager or a DHCP client may quietly hand the system its old DNS servers again.

Hop 06

Troubleshooting dnscrypt-proxy on Linux

  • bind: permission denied: not root; use sudo -s, or su where sudo is missing.
  • bind: address already in use: go back to step zero.
  • systemctl failed: exit status 5: you ran -service start before -service install.
  • An error about IPv6: remove , [::1]:53 from listen_addresses.
  • DNSCrypt servers time out while DoH works: allow outgoing UDP 443.

The last item deserves a word. Networks may pass TCP 443 but drop UDP 443, which DNSCrypt and QUIC both use, so the log shows DNSCrypt servers timing out next to healthy DoH ones. On systems still using iptables, the wiki’s rule iptables -A OUTPUT -p udp --sport 443 -j ACCEPT and a restart fixes it.

Hop 08

Frequently asked questions

Should I use the distribution package or the release tarball?

The package is easier to keep updated with the rest of the system and puts the configuration in /etc/dnscrypt-proxy. The tarball gives you the newest release immediately and the plain service installer the wiki documents. Packages may use systemd socket activation, which upstream treats as unsupported, so issues there go to the package maintainers.

What do the TCP_NODELAY warnings mean?

They appear when systemd socket activation is in use. Systemd tries to apply TCP-only options to the UDP socket and logs that the protocol is not available. The wiki describes them as expected and harmless, and they do not affect how the proxy resolves names. Upstream calls socket activation not well tested and supports the plain service unit.

Why does my resolv.conf keep changing back?

Network managers, resolvconf and DHCP clients rewrite the file when a connection comes up. You can lock it with chattr +i, put the nameserver line into the resolvconf base file, or stop dhclient from requesting domain-name-servers. Keeping systemd-resolved and pointing it at the proxy avoids the fight altogether. Repeat a leak check after a reboot and after reconnecting Wi-Fi.

Can the proxy run as a normal user?

Yes. Set user_name in the configuration file to an existing system account. The proxy starts as root to open port 53 and then drops to that user. Packaged systemd units often achieve the same with DynamicUser, in which case log and cache paths should sit under /var/log/dnscrypt-proxy and /var/cache/dnscrypt-proxy. Keep the executable in a root-owned folder.

↑