Skip to content
Home » ISPs » DNS over TLS: Protect your network with Ubuntu

DNS over TLS: Protect your network with Ubuntu

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 bionic main restricted universe multiverse
deb bionic-security main restricted universe multiverse
deb 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/"
  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: ""
- address_data:
  tls_auth_name: ""

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

Use for security

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

- address_data:
  tls_auth_name: ""
- address_data:
  tls_auth_name: ""

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
    zone_file: /etc/unbound/
    # {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 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 and, 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 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.

If you found this post informative or helpful, please share it!

5 thoughts on “DNS over TLS: Protect your network with Ubuntu”

  1. Just curious why you went with Quad9 DNS servers, rather than’s DNS servers. Do you think that Quad9 combined with the above malware blocking gives equal protection versus CleanBrowsing?

    1. Good point. Quad9 was there out of the box; getting’s servers to work took some doing. I meant to get that in there and it must have slipped my mind. I’ll update it with what I did to get it working with, as that’s a better overall combination.

  2. I did set up a second VM with Ubuntu 18.04.1 Server, and configured Stubby to use’s TLS over DNS. When using dig to complete lookups on items not in the cache, it is much slower than Quad9. Dig averaged about 200ms for a complete lookup on the Unbound server using Quad9, versus around 700ms on the Unbound server using When pinging Quad9’s DNS the response is about 20ms versus 42ms for’s DNS. However, in real world usage from devices hitting Unbound on either Unbound server, I can’t see the slowdown.

    One thing that I did add to my Unbound configs, on both VMs, is the “prefetch: yes” option. From the documentation, Unbound will re-cache expiring look ups on FQDNs that are used often. I don’t know what Unbound considers often though. But given that there are a plethora of Android devices in this household, I’m betting a lot of Google domains are being kept in the cache.

    Looking forward to your additions on CleanBrowsing, as now I’m wondering if my CleanBrowsing config is perfect.

    1. Thanks for the tip on prefetch. I found a couple of other options that seem to help performance, so I updated the unbound config. I also updated the stubby config with the settings I’ve been using for Cleanbrowsing. So far this config is working for me.

  3. That’s also how I have my CleanBrowsing settings. I’ve gone a head and plugged your additional speed changes for Unbound into my configs, and I’ll see how it works. So far, so good.

    I was messing around with other filter lists, and tried out Dan Pollock’s’s hosts file. Something in that file messes with my wife’s phone’s ability to sync her work email, which uses Outlook. So, I had to stop using that list until I can figure out what is causing the issue.

Comments are closed.

%d bloggers like this: