Set up and configure Pi-Hole for network-wide ad blocking

Last updated on

pi-hole

I've been using Pi-Hole for a few years, and I just recently set it up again on a new machine with a new router. It's stupid easy and super effective, here's how.

Sections

  1. Pre-Requisites and Preparations
  2. Installing Pi-Hole bare metal
  3. Installing Pi-Hole in a docker container
  4. Configuring DNS
  5. Using adlists to block domains
  6. Advanced DNS settings
  7. Further steps
  8. Reference

Pre-Requisites and Preparations

Before anything, make sure the machine you’re installing Pi-Hole on has a static IP, the installer will bug you about this too. Also, Pi-Hole will run a web server at ports 80 and 443, for serving the web UI page, so make sure no other web server like Apache or NGinx is running.

Also, when installing Pi-Hole on Ubuntu you may get an error message along the lines of this:

Error starting userland proxy: listen tcp4 0.0.0.0:53: bind: address already in use

This is because Pi-Hole includes dnsmasq which binds to port 53, but Ubuntu by default uses systemd-resolved on that port. If this happens, you’ll need to disable systemd-resolved prior to installing Pi-Hole. Do this:

sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved.service

Then edit the file at /etc/resolv.conf and change nameserver 127.0.0.53 to nameserver 1.1.1.1 (or whatever DNS provider you prefer) to not break DNS resolution. Afterwards use dig google.com or ping google.com to verify DNS is still working, then proceed with installing Pi-Hole.

Installing Pi-Hole bare metal

The quickest and easiest way to install Pi-Hole is via their provided shell script:

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

Executing the script will prompt a number of dialogs, pay attention and make sure you input all the correct information.

Information

Make sure to take note of the admin password provided at the end of the install process, you’ll need it to login to the Web UI. Ideally you should change the admin password with pihole -a -p newpassword.

Now you should be able to access the Pi-Hole Web UI at either http://pi.hole/admin, or use the IP address or hostname, e.g. http://192.168.1.250/admin or http://hostname/admin.

Installing Pi-Hole as a docker container

Like with a bare metal install, make sure the machine you’ll be running Pi-Hole on has a static IP address and no other web server for smoothest install experience. We’ll be using Docker Compose. If you haven’t already (personally I don’t bother using Docker without Compose), install it:

sudo apt update
sudo apt install docker-compose

Now create a new compose file with the command nano docker-compose.yml then copy and paste the below into it:

pihole:
  container_name: pihole
  image: pihole/pihole
  environment:
    - "TZ=America/New_York"
    - "WEBPASSWORD=admin" # change to your password
    - "FTLCONF_LOCAL_IPV4=192.168.0.100" # change to your server IP
    - "DNS1=1.1.1.1"
    - "DNS2=1.0.0.1"
    - "DNSMASQ_LISTENING=all"
    - "DNSSEC=true"
    - "QUERY_LOGGING=true"
  volumes:
    - "~/pihole/config:/etc/pihole/"
    - "~/pihole/dnsmasq:/etc/dnsmasq.d/"
  network_mode: host
  cap_add:
    - NET_ADMIN
  restart: unless-stopped
  # The below label is only necessary if using Watchtower to exclude the Pi-Hole container from automated updates (see https://github.com/pi-hole/docker-pi-hole#note-on-watchtower)
  labels:
    - "com.centurylinklabs.watchtower.enable=false"

Above I include some environmental variables, I’ll explain:

  • TZ=, WEBPASSWORD= and FTLCONF_LOCAL_IPV4= are recommended by the Pi-Hole developers to set the timezone, UI password and server IP address.
  • DNS1= and DNS2= will preset specific upstream DNS resolvers, I suggest Cloudflare but change this to your preference.
  • DNSMASQ_LISTENING=all makes Pi-Hole listen on all interfaces, which should grab network traffic from all devices on your LAN.
  • DNSSEC=true forces Pi-Hole to use DNSSEC if available.
  • QUERY_LOGGING=true sets Pi-Hole to log all queries it receives, this is optional but I like logs, so I always turn it on.

After that are volumes, left of the colon is the local directory you’ll map to the directory inside the container, right of the colon. Change your mapping left of the colon to your prefernece, but make sure to leave the internal maps right of the colon as is!

The parameter network_mode: host is the simplest way to get Pi-Hole working in a container, and is recommended if you’ll be using Pi-Hole to broadcast DHCP. Speaking of which…

cap_add:
  - NET_ADMIN

This is required to use DHCP in Pi-Hole. If you’re not doing so, leave it out and you can also remove network_mode: host, in which case you’ll have to map ports manually and add the below:

ports:
  - "53:53/tcp"
  - "53:53/udp"
  - "80:80/tcp"

Once your docker-compose.yml is ready, use the following command from within the same directory as the yaml file to create and run the Pi-Hole container:

docker-compose up -d

After a minute or so the container should be up and running, which you can confirm with the command docker ps, your output should look something like this:

CONTAINER ID   IMAGE           COMMAND      CREATED          STATUS                    PORTS                                                                                                             NAMES
826fe8b20bdd   pihole/pihole   "/s6-init"   45 seconds ago   Up 41 seconds (healthy)   0.0.0.0:53->53/tcp, :::53->53/tcp, 0.0.0.0:80->80/tcp, 0.0.0.0:53->53/udp, :::80->80/tcp, :::53->53/udp, 67/udp   pihole

Configuring DNS

In order for Pi-Hole to work network-wide for all devices (including phones and tablets on Wi-Fi), you’ll need to configure your router to use the Pi-Hole server as DNS. The method differs for every router, and some do not have the option at all. (AT&T’s Arris BGW210-700 for example does not let you set your own DNS provider.) If the option is available, it’s usually under DHCP Settings. Google is your friend here for instructions on your own hardware.

Information

If your router does not have the option of setting a DNS server, you won’t be able to block ads for all devices on your network automatically. Instead you’ll have to configure each device’s DNS.

DNS settings.

These are my personal settings, I use Cloudflare’s 1.1.1.1 as the upstream DNS, but use which ever you prefer. Under interface settings the recommended setting of “Allow only local requests” is the most secure option and Pi-Hole should work as intended with it checked.

Using adlists to block domains

On the Pi-Hole web UI, click on Adlists on the navigation bar:

Pi-Hole Adlists.

The most efficient way to block URLs in Pi-Hole is to use an adlist, which is a list of URLs to block en masse. (You can also blacklist individual URLs from the Domains section of the UI.) Pi-Hole comes with a default adlist that blocks around 300k URLs, but there’s many more adlists curated by the community. Here are the ones I use:

Once you’ve added all the adlists (and any time you add additional ones), make sure to “update gravity” for the changes to take effect. Go to Tools on the navigation bar, click on Update Gravity, and click the big Update button. Do not leave the page until the process is done!

You may end up with several million “domains on adlists” as shown in the dashboard. Don’t panic. You’ll see your dashboard stats explode with blocked requests, especially from mobile devices. Pay attention to any issues you have visiting websites and using online apps/services that you commonly do, and whitelist domains as needed. (You can also use a curated whitelist.)

Over 3 million domains blocked inon Pi-Hole.

Advanced DNS Settings

By default the dashboard will show all clients as IP addresses, but there’s a few methods to show hostnames instead. (Or hostname.domain) The easiest way is to use Conditional Forwarding, though it does not work with every router.

Go to Settings on the navigation bar, click on the DNS tab, and scroll down to Advanced DNS settings.

Conditional Forwarding settings.

Check the box to Use Conditional Forwarding, enter your network information, and hit Save. Check the dashboard and see if that’s enough to display hostnames instead of IP addresses.

If the hostnames are not showing (sometimes it takes a minute), go back to Advanced DNS settings.

Advanced DNS settings.

The above settings should be checked for more security, but try unchecking one or both to see if they make the hostnames show. If not, it’s possible your router does not broadcast a local domain.

You’ll have to manually add each device’s IP address and hostname/domain. Go to Local DNS on the navigation bar, and click on DNS Records.

Adding new domain and IP.

Alternately, you can manually edit the /etc/hosts file on the server running Pi-Hole. You can bind an IP to a hostname, domain or any other alias.

# /etc/hosts

192.168.0.200   hostname1
192.168.0.215   hostname2
192.168.0.230   mydomain.tld
192.168.0.245   laptop

After saving your changes to the file, use the following command for them to take effect.

pihole restartdns

If you’ve been following the instructions, you’re all set to block ads. Pi-Hole will act as a middleman between you and your chosen DNS (Cloudflare’s 1.1.1.1 for example), blocking ads, tracking and telemetry. If you want to go a bit deeper and make Pi-Hole even better, read on below.

Further steps

Updating Pi-Hole

When an update to Pi-Hole, FTL and/or the Web Interface is available, you can easily update your bare metal Pi-Hole in the terminal by using the command pihole -up. Pi-Hole will not update on it’s own, so you have to do it manually, and the Pi-Hole team does not recommend automating it. (Though you can, if you so choose.)

If you’d rather update Pi-Hole during off-hours, like in the middle of the night, I suggest using at — it lets you use schedule a one-time task for a later time, similar to cron but non-recurring. (The syntax for at is also more human-readable than cron.) For example, the below command will schedule pihole -up to be executed at 5:00 AM:

pihole -up | at 5AM

If running Pi-Hole in a docker container, you don’t need to use pihole -up. Instead, you just need to download a new image — do this to update Pi-Hole if you set it up with Docker Compose:

  • docker-compose pull to pull the latest image
  • docker-compose up -d --no-deps to restart containers with newer image
  • docker system prune --all to delete older unused images (not just Pi-Hole!)

Alternately, or if you used docker run instead of compose to setup Pi-Hole, use these commands:

  • docker pull pihole/pihole to pull the latest image
  • docker restart pihole to restart the container with newer image

If you did not name the container, use docker ps to list all containers and use the CONTAINER ID to restart the container, e.g. docker restart 0ef99ce930bd.

Backup and/or restore Pi-Hole configuration

You may want to regularly create a backup of your Pi-Hole configuration. You can’t automate it, but that’s ok because it’s very simple — just to go the web UI, click on Settings, then go to the Teleporter tab and click the Backup button. This will download a tar.gz file to the computer you’re accessing the web UI from, and within this same screen you can restore from a backup file if necessary. You might consider committing your backup to a private GitHub repo too.

Use local time instead of UTC

If you notice the query log displaying times as UTC instead of your local time zone, and you want the logs to use your time zone, use (for example if you want EST):

sudo timedatectl set-timezone America/New_York

If you want to find out your time zone in the tz database, see here.

Use DNS over HTTPS or DNS over TLS

By default, Pi-Hole sends DNS requests in plain text, which can be seen by your ISP or anyone else snooping on your network traffic. DNS over HTTPS (DoH) and DNS over TLS (DoT) encrypts DNS requests between your Pi-Hole and the upstream DNS resolvers. See my quick guides for setting up DoH (with cloudflared) or DoT (with either cloudflared or unbound) in Pi-Hole. Just make sure you’re using an upstream DNS resolver that supports what you want to use; Cloudflare and Quad9, for example, support both DoH and DoT.

Use Pi-Hole as a recursive DNS

If you want your setup to be more private, consider setting up a recursive DNS with unbound, that way Pi-Hole is forwarding your DNS requests to the authoritative servers, instead of forwarding to public DNS resolvers like Cloudflare and Google. (See the link for more details and official instructions on setting it up.)

Make Pi-Hole update gravity more often

Pi-Hole will automatically update the gravity database every Sunday between 3:00 AM and 5:00 AM, but if you’re using regularly updated community-maintained adlists, you may want to consider using cron to automate updating the gravity every day or two. This is entirely optional, and probably overkill, so don’t feel like you have to do this. Use cronjob -e and copy & paste the below to (for example) update gravity everyday at 5:00AM. (If you don’t know/remember how to create cron expressions off the top of your head, use this handy tool.)

0 5 * * * pihole -g

If you’re running Pi-Hole in a docker container, use this command instead:

0 5 * * * docker exec <pihole_container_name> pihole -g

Run and sync two Pi-Holes

Also, if you make Pi-Hole your primary DNS it becomes a critical part of your network — if it goes down, devices on your network won’t be able to resolve any domains. For this reason, you may want to run another Pi-Hole as a secondary DNS in case the host running your main instance of Pi-Hole crashes. (These things happen.) If your entire network will go down from an issue with Pi-Hole, running a second instance of it makes a lot of sense. If you go this route, I strongly suggest using Gravity Sync to keep the adlists and other settings identical between the two.

Information

Even with all the steps above, your DNS traffic will still go out over plain text and can be seen by your ISP or anyone that happens to be snooping. As such, these may interest you: