(K3S - 6/8) Self-host Pi-Hole on Kubernetes and block ads and trackers at the network level
This article is part of the series Build your very own self-hosting platform with Raspberry Pi and Kubernetes
- Install Raspbian Operating-System and prepare the system for Kubernetes
- Install and configure a Kubernetes cluster with k3s to self-host applications
- Deploy NextCloud on Kuberbetes: The self-hosted Dropbox
- Self-host your Media Center On Kubernetes with Plex, Sonarr, Radarr, Transmission and Jackett
- Self-host Pi-Hole on Kubernetes and block ads and trackers at the network level
- Self-host your password manager with Bitwarden
- Deploy Prometheus and Grafana to monitor a Kubernetes cluster
Pi Hole is a network-wide ad blocker. In a typical home environment, this can cut out almost all ads to all devices in your home, without having to install an ad blocker on every single device. Technically Pi-Hole acts as a DNS sinkhole which filters out unwanted results using a blacklist domains list. Pi-Hole also offers a great admin interface to configure and analyse your network traffic (DNS, DHCP, Black/White list, regex, etc.).
In this new article, we will learn how to deploy Pi-Hole on a Kubernetes self-hosting platform.
In order to run entirely the tutorial, we will need:
- A running Kubernetes cluster (see previous articles if you haven’t set this up yet)
- Access to your router admin console to configure Pi-Hole as DNS or disable DHCP (replaced by Pi-Hole)
We are going to isolate all the Kubernetes objects related to Pi-Hole in the namespace
To create a namespace, run the following command:
$ kubectl create namespace pihole
The first step consists in setting up a volume to store the Pi-Hole config files and data. If you followed the previous articles to install and configure a self-hosting platform using RaspberryPi and Kubernetes, you remember we have on each worker a NFS client pointing to a SSD on
1. Deploy the Persistent Volume (PV)
The Persistent Volume specify the name, the size, the location and the access modes of the volume:
- The name of the PV is
- The size allocated is 500MB
- The location is
- The access is ReadWriteOnce
Create the following file and apply it to the k8 cluster.
$ kubectl apply -f pihole.persistentvolume.yml
You can verify the PV exists with the following command:
$ kubectl get pv
2. Create the Persistent Volume Claim (PVC)
The Persistent Volume Claim is used to map a Persistent Volume to a deployment or stateful set. Unlike the PV, the PVC belongs to a namespace.
Create the following file and apply it to the k8 cluster.
$ kubectl apply -f pihole.persistentvolumeclaim.yml
You can verify the PVC exists with the following command:
$ kubectl get pvc -n pihole
In the next part, we are now going to deploy Pi-Hole using a modified version open-source Helm chart pihole-kubernetes.
**1. Install the repo **
$ helm repo add mojo2600 https://mojo2600.github.io/pihole-kubernetes/ && helm repo update
2. Create a secret to store Pi-Hole admin password
<ADMIN_PASSWORD> by the password of your choice. This password will be used to connect to the Pi-Hole administration interface.
$ kubectl create secret generic pihole-secret \
3. Download the Chart values of the chart locally
Run the following command to download the Chart values into the local file
$ helm show values mojo2600/pihole >> pihole.values.yml
If you open the file, you will see the default configuration values to setup Pi-Hole. Instead of using the flag
--set property=value like before, we will use the file
pihole.values.yml to make all the changes.
4. Update the values
We now need to update a few properties before installing the Helm chart. Open the file
pihole.values.yml and change the following properties (replace the information surrounded by
The network config might be different if your need to get DHCP working with Pi-hole.
a. I can override my router default DNS config
b. I can’t override my router DNS config and I need to enable DHCP on Pi-Hole (disable on my router) to force the devices to use Pi-Hole as DNS.
In this case, we are going to use the flag
privileged=true to let the pod use the node network with root privilege which will enable DHCP through network broadcast on port 67 (more info).
4. Install the Chart
In the part, we will install the Helm chart under the namespace
pihole.values.yml as configuration file.
$ helm install pihole mojo2600/pihole \
After a couple of minutes, check if the pod and service is up and running:
$ kubectl get pods -n pihole -o wide
$ kubectl get services -n pihole -o wide
To access Pi-Hole admin, we are now going to deploy an Ingress, responsible of making accessible a service from outside the cluster by mapping an internal
service:port to a host. To choose a host, we need to configure a DNS like we did for NextCloud “nextcloud.<domain.com>” in the previous article. However, unlike NextCloud, Pi-Hole have no reason to be exposed on the Internet, we can pick a host that will be resolved internally to our Nginx proxy (available at
192.168.0.240 : LoadBalancer IP). The simplest solution is to use nip.io which allows us to map an IP (in our case
192.168.0.240) to a hostname without touching
/etc/hosts or configuring a DNS. Basically it resolves
<ip> without requiring anything else, Magic !
1. Create the file
Create the following Ingress config file
pihole.ingress.yml to map the route
/ to Pi-Hole HTTP service:
2. Deploy the ingress
Deploy the Ingress by applying the file
$ kubectl apply -f pihole.ingress.yaml
You can now access Pi-Hole via pihole.192.168.0.240.nip.io/admin.
Click on Login on the left menu and enter the password configured earlier in
If you configured Pi-Hole to be used as DHCP server, you need to go to Settings / DHCP, enable DHCP, configure the IP range and the IP of your network router.