You need a Raspberry Pi with either Raspberry Pi OS or Ubuntu Server installed. For this guide we’re using Raspberry Pi OS as the operating system (use 64bit if you have Raspberry Pi 3 B+ or newer), but all commands and such should be similar with the other operating systems as well.

Useful tools before you start

SD Card Formatter – Whenever you need to format your SD card, use this tool.

Balena Etcher – Tool to flash the OS to your SD card.

Operating Systems

OSDescriptionDownload
Raspberry Pi OS (desktop, lite)Recommended operating system for most users.https://www.raspberrypi.com/software/operating-systems/
Ubuntu (desktop, server, core)Ubuntu is the most popular linux distribution.https://ubuntu.com/download/raspberry-pi

Let’s begin

Download the image and insert the Raspberry Pi’s micro SD card into your computer meanwhile you wait for the download to finish. When the download is finished, download and run/install balenaEtcher: https://www.balena.io/etcher/

Select the image you downloaded, then select the micro SD card in your computer and click on flash. When it is done you can remove the micro SD card from your computer and insert it into your Raspberry Pi and turn on the power. The Raspberry Pi will now boot up and you can log into it by finding the IP-address from your router and using a terminal.

First time you log into it via SSH your username and password is the default for the operating system you installed showed to the right here.

On the first login you will be asked to change the password which we recommend that you do.

Operating systemUsernamePassword
Ubuntu Serverubuntuubuntu
RaspbianChosen upon first bootChosen when user is created on first boot

If you want to use DietPi follow the guide here: https://dietpi.com/blog/?p=564

And for information on how to enable encrypted DNS with DietPi and Unbound check out this page: https://dietpi.com/docs/software/dns_servers/

Setting up Docker

Type in this in your terminal window:

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done

Set up Docker’s apt repository.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

To install the latest version, run:

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

After Docker is installed make sure to add your user to the docker group.

sudo groupadd docker
sudo usermod -aG docker $USER

Log out and log back in so that your group membership is re-evaluated.

Setting up the DNS server

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. You may also install and run both if you want to have a backup, for example unbound as main DNS and then stubby through Cloudflare as secondary DNS. Both guides below uses different ports to make this possible for those that are interested in this.

Unbound

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 Unbound will become your private DNS resolver.

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: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes

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

    # 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.
    # IP fragmentation is unreliable on the Internet today, and can cause
    # transmission failures when large DNS messages are sent via UDP. Even
    # when fragmentation does work, it may not be secure; it is theoretically
    # possible to spoof parts of a fragmented DNS message, without easy
    # detection at the receiving end. Recently, there was an excellent study
    # >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
    # by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
    # in collaboration with NLnet Labs explored DNS using real world data from the
    # the RIPE Atlas probes and the researchers suggested different values for
    # IPv4 and IPv6 and in different scenarios. They advise that servers should
    # be configured to limit DNS messages sent over UDP to a size that will not
    # trigger fragmentation on typical network links. DNS servers can switch
    # from UDP to TCP when a DNS response is too big to fit in this limited
    # buffer size. This value has also been suggested in DNS Flag Day 2020.
    edns-buffer-size: 1232

    # 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 unnecessary to seek performance enhancement by increasing num-threads above 1.
    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

    # Ensure no reverse queries to non-public IP ranges (RFC6303 4.2)
    private-address: 192.0.2.0/24
    private-address: 198.51.100.0/24
    private-address: 203.0.113.0/24
    private-address: 255.255.255.255/32
    private-address: 2001:db8::/32

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 5353

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

Our next step is to ensure that Pi Hole’s faster-than-light (FTL) daemon adheres to the “edns-packet-max” limit we set in Rebound. To set this, we need to write a config file within the “/etc/dnsmasq.d” directory by using the command below.

sudo nano /etc/dnsmasq.d/99-edns.conf

You will want to add the following line within this file.

edns-packet-max=1232

With the line added, you can save and quit by pressing CTRL + X, followed by Y, and then the ENTER key.

For those running Raspberry Pi OS Debian 11 (Bullseye) and newer, you must disable the “Unbound resolvconf” service that is automatically created during installation.

To disable the “Unbound resolvconf” service, use the following command in the terminal.

sudo systemctl disable --now unbound-resolvconf.service

Next, we must disable the “resolvconf_resolvers.conf” file so that it isn’t evoked by resolvconf itself.

sudo sed -Ei 's/^unbound_conf=/#unbound_conf=/' /etc/resolvconf.conf

For our configuration changes to take effect, you must restart the Unbound service.

sudo service unbound restart

You can test DNSSEC validation using

dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5353

dig sigok.verteiltesysteme.net @127.0.0.1 -p 5353

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 unbound is set up we can continue installing and setting up pi-hole.

Installing Pi-hole

Create a directory for Pi-hole:

mkdir pihole
cd pihole

Inside the folder we are going to create a compose.yml file.

nano compose.yml

In this file copy and paste in the following:

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    # For DHCP it is recommended to remove these ports and instead add: network_mode: "host"
    network_mode: "host"
#    ports:
#      - "53:53/tcp"
#      - "53:53/udp"
#      - "80:80/tcp"
    environment:
      TZ: 'Europe/Oslo'
      FTLCONF_webserver_api_password: 'YOUR_WEB_ADMIN_PASSWORD'
      FTLCONF_dns_upstreams: 127.0.0.1#5353;::1#5353
      FTLCONF_dns_listeningMode: 'all'
      FTLCONF_webserver_port: '80r,443s,[::]:80r,[::]:443s'
      FTLCONF_dns_port: '53'
    # Volumes store your data between container upgrades
    volumes:
      - './etc-pihole:/etc/pihole'
      - './etc-dnsmasq.d:/etc/dnsmasq.d'
    #   https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
    restart: unless-stopped

Hit CTRL + X, click Y to answer yes that you want to save and then hit enter.

To start Pi-hole type the following command while you are inside the pihole/ folder:

docker compose up -d

Pi-hole will now download and then start. To find the password for the admin page you can simply look in the logs by typing:

docker compose logs -f

To go out of the logs simply hit CTRL + C.

Whenever you want to update the Pi-hole in the future do the following:

# 1. First go into the Pi-hole folder
cd pihole
# 2. Next we shutdown Pi-hole
docker compose down
# 3. Now we update the Pi-hole docker
docker compose pull
# 4. Start Pi-hole again
docker compose up -d

Setting up Pi-hole

Whitelisting

Now we should whitelist a couple of pages so that some everyday websites will function as normal. Make sure that you are in the pihole direcotry created earlier. Run the commands below. Remember to swap out YOUR_HOME_FOLDER with your username.

git clone https://github.com/anudeepND/whitelist.git
sudo python3 whitelist/scripts/whitelist.py --dir /home/YOUR_HOME_FOLDER/pihole/etc-pihole/ --docker

Next we will setup a weekly automated update for this whitelist.

sudo nano /etc/crontab

At the very end of the file add the following line and swap out YOUR_HOME_FOLDER with your actual home folder.

0 1 * * */7     root python3 /home/YOUR_HOME_FOLDER/pihole/whitelist/scripts/whitelist.py --dir /home/YOUR_HOME_FOLDER/pihole/etc-pihole/ --docker

Next 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 allow 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 api.epicgames.dev r3.o.lencr.org

Adlists

We recommend that you add the following adlists in addition to the default ones. Go into your Pi-hole admin page -> Group Management -> Adlists and add the lists you want.

OISD

OISD is a collection many adlists from accross the Internet, and all domains in this list are verified. There shouldn’t be any false positives in this list.

https://big.oisd.nl/

Block List Project

The Block List Project maintains many different blocking lists. We recommend at least adding the following lists: Ads, Fraud, Malware, Phising, Ransomware, Scam and Tracking.

# Ads
https://blocklistproject.github.io/Lists/ads.txt
# Fraud
https://blocklistproject.github.io/Lists/fraud.txt
# Malware
https://blocklistproject.github.io/Lists/malware.txt
# Phising
https://blocklistproject.github.io/Lists/phishing.txt
# Ransomware
https://blocklistproject.github.io/Lists/ransomware.txt
# Scam
https://blocklistproject.github.io/Lists/scam.txt

Phising Army

Phising Army is devoted to fight the criminals and provides protection against phising and malware sites, and so on. 91% of cyber attacks happens through email with links to malware and phising sites. We recommend using the extended blocklist that they provide.

https://phishing.army/download/phishing_army_blocklist_extended.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.

Pi 4 MazePro Case

If you have Raspberry Pi 4 with the MazePro Case and want to use the power button on that case to also safely shutdown your Pi 4, then type in the following command and hit enter:

sudo echo "dtoverlay=gpio-shutdown" >> /boot/config.txt

Next reboot your Pi 4 for the changes to take effect.