Right now the only consumer OS that supports DNS over TLS is Android Pie 0r newer. But that doesn’t mean you can’t run DNS over TLS yourself to protect your DNS privacy. Here’s how to implement a local DNS server that uses TLS to talk to upstream DNS servers to keep your ISP and other hostile third parties from seeing your DNS lookups. As a bonus, it caches your requests to speed up your Internet connection, and you can filter out malware domains with it.

There’s a lot to like with this setup.

DNS over TLS

This DNS server isn’t much to look at, but being able to block bad domains and turn all DNS queries into DNS over TLS is very nice.

There are actually a number of benefits to running your own DNS server, besides encrypting your outbound DNS traffic via DNS over TLS. The packages we’ll be using cache your queries, so you’ll probably actually see a performance boost once the cache gets populated. Even if you have gigabit Internet, a caching nameserver on your local network is closer and faster than any other. It also gives you the opportunity to do some of your own filtering. In my example I’ll show you how to filter malware domains to protect yourself from bad actors and at least interfere with the operation of malware, if not keep it off your network entirely.

If you have Linux or Unix experience, this won’t be a hard project for you. If you don’t, this is a good way to get some experience with both Linux and DNS administration.

Install Ubuntu 18.04

Since Ubuntu has all of the packages you need, I recommend installing Ubuntu 18.04. I used a virtual machine, and it doesn’t need to be a very big VM. I used 1 CPU, 10 GB of disk space and 1 GB of RAM but probably could have gotten away with less disk space and RAM. You can also install it directly on a computer too.

You could also do this with a Raspberry Pi and Ubuntu MATE.

Assign a static IP address

You’ll want to assign your local DNS server a static IP address. It’s a little different in Ubuntu than in other Linux distros.

Disable systemd-resolve

Issue these two commands to prevent systemd’s resolver from interfering with unbound and stubby:

systemctl disable systemd-resolved.service

service systemd-resolved stop

Install unbound

Unbound is an up and coming DNS server. Its name plays off a popular DNS server named BIND. Unbound aims to be more secure and faster than BIND. I like things that are fast and secure.

We’ll be using unbound to provide two things: caching and blacklisting. This lets us download malware domain lists and blacklist them. If you use the right upstream servers they’re probably blocking those too, but this provides insurance just in case. I’ll also show you how to blacklist sites you always want to block, which is nice.

Caching speeds things up which is good because DNS over TLS adds overhead. The first time you or someone on your network connect to a site you may experience a bit more latency than usual, but the configuration of the server will try to claw as much performance back as it can.

To install unbound, run apt-get install unbound.

Install stubby

While unbound has some support for DNS over TLS, it’s not as reliable or as fast as another tool called stubby. So we’ll configure unbound to handle blacklisting and caching, then hand the work of talking to the upstream DNS servers over to stubby. Stubby is the tool that will actually handle talking to your remote servers using DNS over TLS on port 853.

apt-get install stubby ought to suffice, it didn’t on my Ubuntu box. First I had to edit /etc/apt/sources.list:

deb http://archive.ubuntu.com/ubuntu bionic main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu bionic-security main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu bionic-updates main restricted universe multiverse

After that, running the command apt-get install stubby worked like a champ.

Configure unbound

Here’s a working /etc/unbound.conf. If it fails, you didn’t disable systemd-resolved.

  access-control: allow
  use-syslog: yes
  directory: "/etc/unbound"
  # Speed and privacy
  minimal-responses: yes
  prefetch: yes
  qname-minimisation: yes
  rrset-roundrobin: yes  do-not-query-localhost: no
  include: "/etc/unbound/blackhole.zone"
  name: "."
    forward-addr: ::1@8053

You’ll need to edit line 3 to match your network. If your home network lives on, which is somewhat common, change line 3 to reflect that.

To restart unbound after editing the configuration, use the command systemctl restart unbound.

Configure stubby

Open /etc/stubby/stubby.yml in your text editor. Scroll down to the section labeled listen_addresses and change it to this:

- 0::1@8053

Then scroll to the section labeled upstream_recursive_servers. Comment out the default servers, as they’re rather slow.

Using Quad-9 for performance

To get high performance with Stubby, use Quad-9’s servers. Scroll down to the Quad-9 servers and uncomment those lines:

# IPv4 addresses
## Quad 9 'secure' service - Filters, does DNSSEC, doesn't send ECS
- address_data:
  tls_auth_name: "dns.quad9.net"
- address_data:
  tls_auth_name: "dns.quad9.net"

#IPv6 addresses
## Quad 9 'secure' service - Filters, does DNSSEC, doesn't send ECS
- address_data: 2620:fe::fe
  tls_auth_name: "dns.quad9.net"

Use cleanbrowsing.org for security

If you want high security, use cleanbrowsing.org’s servers. Add a section below the Quad-9 section.

- address_data:
  tls_auth_name: "security-filter-dns.cleanbrowsing.org"
- address_data:
  tls_auth_name: "security-filter-dns.cleanbrowsing.org"

Finishing up

If you wish, you can delete all the commented lines in that section. To restart stubby after editing the configuration, issue the following two commands:

kill -9 $(pidof stubby)

stubby -g

Blackhole some malware domains

There’s a nice script to pull down some malware domain lists and convert them into a format that unbound can use. It will also handle your whitelists and blacklists for you. First, install Python and pip:

apt install python-pip

pip install dns-blackhole

Next you need a config file in /etc/dns-blackhole/dns-blackhole.yml

    cache: /var/cache/dns-blackhole
    log: /var/log/dns-blackhole/dns-blackhole.log
    whitelist: /etc/dns-blackhole/whitelist
    blacklist: /etc/dns-blackhole/blacklist
      hosts: &bh_hosts
        - http://www.malwaredomainlist.com/hostslist/hosts.txt
    zone_file: /etc/unbound/blackhole.zone
    # {domain} will be replaced by the blackholed domain, do not change it here
    zone_data: 'local-zone: "{domain}" always_nxdomain'
      hosts: *bh_hosts

Then execute the command dns-blackhole.

If you want some more malware domain lists, Lenny Zeltser has a large list of feeds. Not all are compatible with the dns-blackhole script. But looking at the feeds on his site, they all seemed pretty reasonable. I found some others that are so aggressive it kind of defeats the point of having an Internet connection. More on that in a bit.

Configure a whitelist and blacklist

The tool dns-blackhole stores whitelists and blacklists at /etc/dns-blackhole/whitelist and /etc/dns-blackhole/blacklist. It just accepts a plaintext list of domains, one per line, that should always be allowed or blocked no matter what your feeds say.

For example, if you never want to allow Facebook, you could simply add facebook.com to your blacklist, and then your DNS server will never resolve it. By the same token, I found an anti-tracking feed that had a thing against search engines. Seriously, it blocked every search engine that I know of, including Duck Duck Go, which explicitly says it doesn’t track you. I could whitelist google.com and bing.com, but I decided that any feed that blocks even Duck Duck Go probably was blocking way too many other things too.

I wouldn’t go too crazy with the whitelist but you might choose to add a handful of your family’s favorite sites in there to protect yourself from a contributor with a heavy hand. If my kids get up one morning and Roblox doesn’t work, I’m going to be getting up to fix it, whatever hour it might be.

Any time you update your whitelist or blacklist, you have to run dns-blackhole again, then bounce unbound with the command systemctl restart unbound.

Schedule updates

Pulling down lists of bad sites is great, but an outdated list gives you a false sense of security and might stop legitimate sites from working.

Issue the command export EDITOR=nano and then the command crontab -e and add these two lines to the end:

01 04 * * * /usr/local/bin/dns-blackhole
30 04 * * * /bin/systemctl restart unbound

This will update your DNS blackhole at 4:01 AM every morning, then restart your DNS server at 4:30 to make the updates take effect.


If unbound ever fails to start, use the command journalctl -u unbound and scroll to the end to view its log. The configuration I provided you ought to be pretty solid, but I sure made some mistakes along the way. Some tools will ignore a bad line in a config file, but unbound won’t.

Testing your server

To test your server, run the command nslookup from a command prompt on any computer on your network. Type the command server followed by your new DNS server’s IP address, for example, server Next, type a domain name. I like to use yahoo.com as a test, since it resolves to so many IP addresses. If you get responses, your server is working. If you don’t get responses, chances are you have an error in a config file somewhere or didn’t disable systemd-resolve.

Once you’re confident your server works properly, configure some of your other computers to use it. That varies by operating system so I’ll leave that part to you.