You need a Raspberry Pi with either rasbian or ubuntu installed. We recommend installing ubuntu (64bit if you have Raspberry Pi 3 B+ or newer): https://ubuntu.com/download/raspberry-pi

Open up a terminal window and choose one of the installs below (stubby or unbound). If you are concerned about privacy and don’t trust public DNS resolvers you should go with unbound.

Stubby is “an application that acts as a local DNS Privacy stub resolver (using DNS-over-TLS). Stubby encrypts DNS queries sent from a client machine (desktop or laptop) to a DNS Privacy resolver increasing end user privacy.” Stubby is basically an encryption stub that encrypts the DNS traffic between you and an upstream resolver.

For this guide, Cloudflare DNS is chosen, but you can choose whatever DNS provider you prefer/trust.

Unbound is “a caching DNS resolver.” It directly communicates with the authoritative name servers and does the resolving itself, avoiding the need for a upstream resolver. It has very efficient caching and is generally quite fast.

For this guide you don’t need to think about public DNS resolvers as your Raspberry Pi will become your private DNS resolver.

sudo apt install stubby -y

The main configuration file is /etc/stubby/stubby.yml. You can open the file with:

sudo nano /etc/stubby/stubby.yml

The following line makes stubby run as a stub resolver instead of a full recursive resolver, which is why it’s named stubby.

resolution_type: GETDNS_RESOLUTION_STUB

The following configuration make stubby send DNS queries encrypted with TLS. It will not send quries in plain text.

dns_transport_list:
- GETDNS_TRANSPORT_TLS

This following line requires a valid TLS certificate on the remote recursive resolver.

tls_authentication: GETDNS_AUTHENTICATION_REQUIRED

The following lines set the listen addresses for the stubby daemon. By default, IPv4 and IPv6 are both enabled.

listen_addresses:
- 127.0.0.1
- 0::1

Standard port is 53, but we’re going to change this to 5053. Edit the listen addresses so it looks like this:

listen_addresses:
- 127.0.0.1@5053
- 0::1@5053

The following line make stubby query recursive resolvers in a round-robin fashion. If set to 0, Stubby will use each upstream server sequentially until it becomes unavailable and then move on to use the next.

round_robin_upstreams: 1

By default there are 3 recursive resolvers enabled in stubby configuration file. They are run by stubby developers and support DNS over TLS. You can choose to leave them enabled or disable them by putting an # in front.

Scroll down to the upstream_recursive_servers: section and add the following text above other DNS servers.

# CloudFlare servers
  - address_data: 1.1.1.1
    tls_auth_name: "cloudflare-dns.com"
  - address_data: 1.0.0.1
    tls_auth_name: "cloudflare-dns.com"

Then find the following line:

round_robin_upstreams: 1

Change 1 to 0. This will make stubby always use CloudFlare DNS server. If CloudFlare is not available, stubby will use other DNS servers.

Save the file and restart stubby for the changes to take effect and test that it’s operational.

sudo systemctl restart stubby
dig pi-hole.net @127.0.0.1 -p 5053

You can test DNSSEC validation using

dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5053
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5053

The first command should give a status report of SERVFAIL and no IP address. The second should give NOERROR plus an IP address.

sudo apt install unbound -y

The main configuration file is /etc/unbound/unbound.conf.d/pihole.conf. You can open the file with:

sudo nano /etc/unbound/unbound.conf.d/pihole.conf

Paste in the follwing configuration in your pihole.conf file:

server:
# If no logfile is specified, syslog is used
# logfile: "/var/log/unbound/unbound.log"
verbosity: 0

interface: 127.0.0.1
port: 5053
do-ip4: yes
do-udp: yes
do-tcp: yes

# May be set to yes if you have IPv6 connectivity
do-ip6: no

# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no

# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
#root-hints: "/var/lib/unbound/root.hints"

# Trust glue only if it is within the server's authority
harden-glue: yes

# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes

# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no

# Reduce EDNS reassembly buffer size.
# Suggested by the unbound man page to reduce fragmentation reassembly problems
edns-buffer-size: 1472

# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes

# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be>
num-threads: 1

# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 1m

# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10

Save the file and restart your local recursive server for the changes to take effect and test that it’s operational:

sudo service unbound restart
dig pi-hole.net @127.0.0.1 -p 5053

The first query may be quite slow, but subsequent queries, also to other domains under the same TLD, should be fairly quick.

You can test DNSSEC validation using

dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5053
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5053

The first command should give a status report of SERVFAIL and no IP address. The second should give NOERROR plus an IP address.

Now that stubby or unbound is set up we can start installing pi-hole. So type in this:

curl -sSL https://install.pi-hole.net | bash

When the installation is done open a browser window and type in your raspberry pi’s IP-address (example: 192.168.1.200/admin) to log into your pi-hole admin page. You can change the password by entering the following command:

pihole -a -p

If you want to remove the password, just hit enter after running the command above.

Then go to settings and click on DNS and change everything as done in the picture below.

Screenshot of Pi-hole configuration

Remember to click save at the bottom of the page afterwards.

Whitelisting

Now we should whitelist a couple of pages so that some everyday websites will function as normal.

First off you should take a look at pihole’s own list over commonly whitelisted domains and either add all or those you need: https://discourse.pi-hole.net/t/commonly-whitelisted-domains/212

If you’re lazy and don’t want to check out Pihole’s own list and copy and paste many times over, we’ve compiled one list of the most important domains here which will fix issues with the following services: Google (Maps, Youtube, etc), Microsoft (Windows, Office, Skype, etc), Spotify, Facebook, Plex, Sonarr, Dropbox, Apple (ID, Music, etc), NVIDIA GeForce Experience, Grand Theft Auto V Online, Epic Games Store, Slack, Mozilla Firefox Tracking Protection, Twitch. Just copy and paste in the code below in your terminal window on the Raspberry Pi.

pihole -w clients4.google.com clients2.google.com s.youtube.com video-stats.l.google.com android.clients.google.com reminders-pa.googleapis.com firestore.googleapis.com googleapis.l.google.com dl.google.com www.msftncsi.com outlook.office365.com products.office.com c.s-microsoft.com i.s-microsoft.com login.live.com login.microsoftonline.com g.live.com dl.delivery.mp.microsoft.com geo-prod.do.dsp.mp.microsoft.com displaycatalog.mp.microsoft.com sls.update.microsoft.com.akadns.net fe3.delivery.dsp.mp.microsoft.com.nsatc.net clientconfig.passport.net v10.events.data.microsoft.com v20.events.data.microsoft.com client-s.gateway.messenger.live.com xbox.ipv6.microsoft.com device.auth.xboxlive.com www.msftncsi.com title.mgt.xboxlive.com xsts.auth.xboxlive.com title.auth.xboxlive.com ctldl.windowsupdate.com attestation.xboxlive.com xboxexperiencesprod.experimentation.xboxlive.com xflight.xboxlive.com cert.mgt.xboxlive.com xkms.xboxlive.com def-vef.xboxlive.com notify.xboxlive.com help.ui.xboxlive.com licensing.xboxlive.com eds.xboxlive.com www.xboxlive.com v10.vortex-win.data.microsoft.com settings-win.data.microsoft.com s.gateway.messenger.live.com client-s.gateway.messenger.live.com ui.skype.com pricelist.skype.com apps.skype.com m.hotmail.com sa.symcb.com s{1..5}.symcb.com officeclient.microsoft.com spclient.wg.spotify.com apresolve.spotify.com upload.facebook.com creative.ak.fbcdn.net external-lhr0-1.xx.fbcdn.net external-lhr1-1.xx.fbcdn.net external-lhr10-1.xx.fbcdn.net external-lhr2-1.xx.fbcdn.net external-lhr3-1.xx.fbcdn.net external-lhr4-1.xx.fbcdn.net external-lhr5-1.xx.fbcdn.net external-lhr6-1.xx.fbcdn.net external-lhr7-1.xx.fbcdn.net external-lhr8-1.xx.fbcdn.net external-lhr9-1.xx.fbcdn.net fbcdn-creative-a.akamaihd.net scontent-lhr3-1.xx.fbcdn.net scontent.xx.fbcdn.net scontent.fgdl5-1.fna.fbcdn.net graph.facebook.com b-graph.facebook.com connect.facebook.com cdn.fbsbx.com api.facebook.com edge-mqtt.facebook.com mqtt.c10r.facebook.com portal.fb.com star.c10r.facebook.com star-mini.c10r.facebook.com b-api.facebook.com fb.me bigzipfiles.facebook.com l.facebook.com plex.tv tvdb2.plex.tv pubsub.plex.bz proxy.plex.bz proxy02.pop.ord.plex.bz cpms.spop10.ams.plex.bz meta-db-worker02.pop.ric.plex.bz meta.plex.bz tvthemes.plexapp.com.cdn.cloudflare.net tvthemes.plexapp.com 106c06cd218b007d-b1e8a1331f68446599e96a4b46a050f5.ams.plex.services meta.plex.tv cpms35.spop10.ams.plex.bz proxy.plex.tv metrics.plex.tv pubsub.plex.tv status.plex.tv www.plex.tv node.plexapp.com nine.plugins.plexapp.com staging.plex.tv app.plex.tv o1.email.plex.tv o2.sg0.plex.tv dashboard.plex.tv gravatar.com thetvdb.com themoviedb.com services.sonarr.tv skyhook.sonarr.tv download.sonarr.tv apt.sonarr.tv forums.sonarr.tv placehold.it placeholdit.imgix.net dl.dropboxusercontent.com ns1.dropbox.com ns2.dropbox.com itunes.apple.com s.mzstatic.com appleid.apple.com gfwsl.geforce.com dev.virtualearth.net ecn.dev.virtualearth.net t0.ssl.ak.dynamic.tiles.virtualearth.net t0.ssl.ak.tiles.virtualearth.net android.clients.google.com connectivitycheck.android.com android.clients.google.com clients3.google.com connectivitycheck.gstatic.com msftncsi.com www.msftncsi.com ipv6.msftncsi.com captive.apple.com gsp1.apple.com www.apple.com www.appleiphonecell.com prod.telemetry.ros.rockstargames.com tracking.epicgames.com tracking-protection.cdn.mozilla.net s.amazon-adsystem.com c.amazon-adsystem.com countess.twitch.tv pubsub-edge.twitch.tv cdn-gl.imrworldwide.com files.slack.com

We also recommend the whitelist from https://github.com/anudeepND/whitelist to be installed with automatic update. So continue with the following steps:

cd /opt/
sudo git clone https://github.com/anudeepND/whitelist.git

Make the script to run the script at 1AM on the last day of the week

sudo nano /etc/crontab

Add this line at the end of the file:
0 1 * * */7 root /opt/whitelist/scripts/whitelist.py

CTRL + X then Y and Enter

sudo python3 whitelist/scripts/whitelist.py

Adlists

For adlists we recommend that you add the DBL list from OISD (this list should be the only one you need as this is a collection many adlists from accross the Internet, all verified). Go into your Pi-hole admin page -> Group Management -> Adlists and add the DBL list url there.

Optional, you can also add some of the lists from Firebog’s ticked list, but keep in mind some of those links are already in the OISD list so it’s no point in adding them for a second time since they are already included in OISD list

The following adlists will block YouTube ads:

https://www.sunshine.it/blacklist.txt
https://raw.githubusercontent.com/kboghdady/youTube_ads_4_pi-hole/master/youtubelist.txt

Congratulations

That’s it. Now all you need to do to protect your network is to point your DNS in the LAN settings of your router towards the IP address of your pi-hole and your whole network will have increased privacy and be free of ads.

PoE hat fan control

On Ubuntu Server 20.04 LTS the dtparams setting does not work, instead you need a udev rule to control the PoE hat fan. Log into your Raspberry Pi via SSH and run the following code to make sure it says “rpi-poe-fan”.

cat /sys/class/thermal/cooling_device0/type

Once you’ve confirmed that we will create a new udev rule. Start so by typing.:

sudo nano /etc/udev/rules.d/50-rpi-fan.rules

Then you can use this udev rule as an example:

SUBSYSTEM=="thermal"
KERNEL=="thermal_zone0"

# If the temp hits 60c, turn on the fan. Turn it off again when it goes back down to 55.
ATTR{trip_point_3_temp}="60000"
ATTR{trip_point_3_hyst}="5000"
#
# If the temp hits 68c, higher RPM.
ATTR{trip_point_2_temp}="68000"
ATTR{trip_point_2_hyst}="2000"
#
# If the temp hits 80c, higher RPM.
ATTR{trip_point_1_temp}="80000"
ATTR{trip_point_1_hyst}="2000"
#
# If the temp hits 81c, highest RPM.
ATTR{trip_point_0_temp}="81000"
ATTR{trip_point_0_hyst}="5000"

Save the file and exit the editor. The last thing you want to do is to apply the udev rules, do so by running the following command:

sudo udevadm control --reload-rules && sudo udevadm trigger

Accessing pihole with HTTPS

If you have a domain and would like to access your pihole from outside your network with HTTPS, this is how you continue to set it up.

sudo apt install certbot
sudo certbot certonly --webroot -w /var/www/html -d your.domain.name

Enter your email address. Then hit A and press enter. And then hit N and press enter.

sudo cat /etc/letsencrypt/live/your.domain.name/privkey.pem \
/etc/letsencrypt/live/your.domain.name/cert.pem | \
sudo tee /etc/letsencrypt/live/your.domain.name/combined.pem

Next, ensure the lighttpd user www-data can read the required certificates:

sudo chown www-data -R /etc/letsencrypt/live

Now, place the following into /etc/lighttpd/external.conf (again, making sure to subsitute your.domain.name for your FQDN):

$HTTP["host"] == "your.domain.name" {
  # Ensure the Pi-hole Block Page knows that this is not a blocked domain
  setenv.add-environment = ("fqdn" => "true")

  # Enable the SSL engine with a LE cert, only for this specific host
  $SERVER["socket"] == ":443" {
    ssl.engine = "enable"
    ssl.pemfile = "/etc/letsencrypt/live/your.domain.name/combined.pem"
    ssl.ca-file =  "/etc/letsencrypt/live/your.domain.name/fullchain.pem"
    ssl.honor-cipher-order = "enable"
    ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    ssl.use-sslv2 = "disable"
    ssl.use-sslv3 = "disable"       
  }

  # Redirect HTTP to HTTPS
  $HTTP["scheme"] == "http" {
    $HTTP["host"] =~ ".*" {
      url.redirect = (".*" => "https://%0$0")
    }
  }
}

Finally, be sure to run sudo service lighttpd restart after this change has been made.