Tutorial: Anonymous DNS requests with Pi-Hole and DNSCrypt

Pi-Hole alone adds a lot of privacy enhancements for your home, but one thing remains regarding the DNS resolvers. They will always know your IP-address and can bind this information to the DNS-query - until now!

Here we go again with a new article about how I use my Pi-Hole, the beloved DNS-Filter-Service which keeps our homenetwork ad- and trackerfree. But this time, we will look deeper at the way we actually resolve the IP addresses of our queries.

Pi-Hole alone adds a lot of privacy enhancements for your home, but one thing remains regarding the DNS resolvers. They will always know your IP-address and can bind this information to the DNS-query. And encrypted DNS protocols like DNS-over-TLS (DoT) or DNS-over-HTTPS (DoH) don't change it, because the encryption is just implemented for the DNS-traffic. The resolver still needs to decrypt it, to read it, and answer your request. So, as you can see, every DNS resolver could create a profile about your online habbits and maybe share it.

A solution for this already exists: there is Oblivious-DNS-Over-HTTPS (ODoH) and DNSCrypt via an anonymous relay host. Both of these ways will hide your IP-address from the resolver, because they will send the query through another host (encrypted!), and the resolver will just get the relay's IP, instead of yours.

I will focus on the DNSCrypt to anon-relay option in this article.

How it works?

As you can see in my poor try to make a graphic, the traffic (DNS-query) itself stays encrypted on its way to the resolver. The DNS resolver gets the connection from the relay and therefore your IP isn't send to the resolver.

The answer works the same: After the resolver decrypted the query, it answers the IP address of the domain encrypted again to the relay. The relay sends it, still encrypted, to you.

So the only host, which could log your IP is the relay, but as the traffic stays encrypted it can't see its details, and therefore I recommend to not use the relay of the same provider as the resolver.

Let's start!

What to do?

  • install dnscrypt-proxy on your Pi (or any other machine)
  • set dnscrypt-proxy up to use the DNSCrypt-DNS-Resolver via an anonymous relay
  • test the setup
  • edit your Pi-Hole settings

Installation of dnscrypt-proxy

Visit https://github.com/DNSCrypt/dnscrypt-proxy/releases/ and download the latest version according to your system.

wget https://github.com/DNSCrypt/dnscrypt-proxy/releases/download/2.1.3/dnscrypt-proxy-linux_arm64-2.1.3.tar.gz

extract it:

tar -xf dnscrypt-proxy-linux_arm64-2.1.3.tar.gz

and we can move it in /opt/ for example, or whereever you want to have it installed.

mv linux-arm64/ /opt/dnscrypt-proxy

Configuration of dnscrypt-proxy

First of all, we create our own config.toml out of the given example:

cd /opt/dnscrypt-proxy

cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml

And we edit it with our favorite text editor

nano dnscrypt-proxy.toml

Please read through all the comments inside the configuration and edit it according to your needs. Here is my config file as another example:

server_names = ['meganerd','meganerd-ipv6','ams-dnscrypt-nl','ams-dnscrypt-nl-ipv6','dct-nl1','sth-dnscrypt-se-ipv6']

listen_addresses = ['[::]:55']

max_clients = 250
ipv4_servers = true
ipv6_servers = true
dnscrypt_servers = true
doh_servers = false
odoh_servers = false

require_dnssec = true
require_nolog = true
require_nofilter = true

disabled_server_names = []

force_tcp = false
timeout = 5000
keepalive = 30
lb_strategy = 'p2'
lb_estimator = true
cert_refresh_delay = 240

bootstrap_resolvers = ['', '']
ignore_system_dns = true
netprobe_timeout = 60
netprobe_address = ''

log_files_max_size = 10
log_files_max_age = 7
log_files_max_backups = 1

block_ipv6 = false
block_unqualified = true
block_undelegated = true
reject_ttl = 10

cache = true
cache_size = 4096
cache_min_ttl = 2400
cache_max_ttl = 86400
cache_neg_min_ttl = 60
cache_neg_max_ttl = 600




  format = 'tsv'


  format = 'tsv'







    urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v3/public-resolvers.md', 'https://ipv6.download.dnscrypt.info/resolvers-list/v3/public-resolvers.md', 'https://download.dnscrypt.net/resolvers-list/v3/public-resolvers.md']
    cache_file = 'public-resolvers.md'
    minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
    refresh_delay = 72
    prefix = ''

    urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v3/relays.md', 'https://download.dnscrypt.info/resolvers-list/v3/relays.md', 'https://ipv6.download.dnscrypt.info/resolvers-list/v3/relays.md', 'https://download.dnscrypt.net/resolvers-list/v3/relays.md']
    cache_file = 'relays.md'
    minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
    refresh_delay = 72
    prefix = ''


fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'cleanbrowsing-adult', 'cleanbrowsing-adult-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-security', 'cleanbrowsing-security-ipv6']



 routes = [
    { server_name='meganerd', via=['anon-cs-berlin', 'anon-cs-de', 'anon-ams', 'anon-cs-austria'] },
    { server_name='ams-dnscrypt-nl', via=['anon-meganerd', 'anon-scaleway-ams'] },
    { server_name='dnscrypt-ch-blahdns-ipv4', via=['anon-cs-berlin', 'anon-scaleway-ams'] },
    { server_name='dct-nl1', via=['anon-meganerd', 'anon-scaleway-ams'] },
    { server_name='ams-dnscrypt-nl-ipv6', via=['anon-meganerd-ipv6', 'anon-scaleway-ams-ipv6'] },
    { server_name='sth-dnscrypt-se-ipv6', via=['anon-meganerd-ipv6', 'anon-scaleway-ams-ipv6'] },
    { server_name='meganerd-ipv6', via=['anon-acsacsar-ams-ipv6', 'anon-dnscrypt.uk-ipv6'] }

skip_incompatible = true



This is how i've configured dnscrypt-proxy. Important is, that you first edit, at the very beginning, the server_names. My listen_addresses are configured to listen on all (v4 and v6) IPs on port 55. And at the very end of the configuration I've set up the routes to add an anonymous relay to the routine. I know, that I don't need to use the IPv4 server combinations, because I'm using IPv6, but I thought I'll let them in the configuration to show you more, how it works.

Add dnscrypt-proxy as a system service and test it

To add it as a system service I use:

./dnscrypt-proxy -service install

We directly start the service:

service dnscrypt-proxy start

And test, if it's running after a bit of time:

service dnscrypt-proxy status

If it shows an active (running) state, we can also test, if a DNS Query gets answered correctly:

dig @ -p 55 hndrk.blog

If the answer looks like this we can move on to edit our Pi-Hole

Add dnscrypt-proxy to your Pi-Hole

Open your Pi-Hole and pull up it's settings. Open the DNS tab and add the dnscrypt-proxy listening on port 55 to your configuration like this:

So, basically, if you use your Internet devices now, everything should be set up correctly. I'll do another test now and see if my configured DNS servers are used with a so called DNS-Leak-Test. (browserleaks.com)

So the recognized DNS server is one of my configured ones. Everything works fine! Yey!


So what we did:

  • install and configure dnscrypt-proxy to use DNSCrypt with an anonymous relay
  • edit Pi-Hole to use this network wide

What we got:

  • anonymously send encrypted DNS-Queries to the DNS Resolvers

Awesome, thanks for reading as always. Love y'all.

Related Links

List of Resolvers: https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v3/public-resolvers.md

List of Relays: https://github.com/DNSCrypt/dnscrypt-resolvers/blob/master/v3/relays.md

Pi-Hole: https://pi-hole.net/

DNSCrypt Project: https://dnscrypt.info/