Let’s use microk8s for kubernetes to host pihole + DNS over HTTPs (DoH)

Let’s use microk8s for kubernetes to host pihole + DNS over HTTPs (DoH). A few years ago, I hit my limit on the Internet advertising. You know, you do a search for something, and then all of a sudden you’re getting presented with all these personalized ads for that thing… everywhere you go. So I fired up a docker container to “try out” pihole on my raspberry pi. It was quick and it worked.

My next evolution was to prevent my ISP (or anyone on the Net) from sniffing my DNS traffic. I used CloudFlared as an HTTPS tunnel/proxy to the CloudFlare DNS servers (they do not log traffic.) Then I wired up pihole to proxy its upstream DNS to use this CloudFlared tunnel. I put all of this into a docker-compose file and that “just worked.”

Over time, I spun up other things such as influxdb, grafana, home-assistant, ads/b tracking, jellyfin, and a bunch of other things. All on various raspberry pi’s hosting docker instances. It was getting… messy.

I’ve now standardized on using Microk8s for container orchestration and management mixed with git-ops for infrastructure automation.

I see a lot of “here’s how you configure pihole” to run as a docker container, but there isn’t much out there for using microk8s. Here’s my contribution.

kubernetes cloud

Getting Started


All of the files are available in my git repo:

MicroK8s for our kubernetes container orchestration and management

For microk8s, make sure the following add-ons are enabled:

  • dns
  • ingress
  • metallb

MetalLb is a load balancer that allows us to assign a fixed ip address “in front of” our k8 pods. K8 will handle the mapping to the proper node (for clusters) and pod. We just use the assigned load balancer ip.

Follow the tutorial and make note of whatever ip address pool you assign to metallb. It should be an unused range on your network (i.e. outside of any DHCP scope or other statically assigned addresses.)

Setting up the pi-hole application in Microk8s (k8)

kubectl apply -f pihole-namespace.yml

Best practice is to use K8 namespaces to segment up your cluster resources. Our first step is to create our pihole namespace

When the pod hosting our pihole container is running it will need disk storage. My k8 is setup to use a NFS server for storage. If you are using host-path or just want ephemeral storage, edit the file and replace nfs-csi with “” (a quoted empty string)

kubectl apply -f pihole-pvc-pihole.yml
kubectl apply -f pihole-pvc-dnsmasq.yml

Pihole uses two files:

  1. adlists.list is used during the very first bootstrapping to populate the gravity database with the domains to blacklist.
  2. custom.list is used for local dns entries. For instance, if you get tired of remembering various ip addresses on your network, you can make an entry in this file to map the ip address to a fully-qualified-domain-name.

We are going to use a k8 feature called a ConfigMap. Later, we will “volumeMount” these configMaps into the pod’s filesystem. Run the helper scripts. If you get an error about not finding the kubectl command, just copy the command from the script file and run in your terminal window.


This step creates a “deployment.” We’re gonna spin up two containers in the pod:

  1. Cloudflared – this creates our HTTPs tunnel to the CloudFlare DNS servers
  2. Pihole – this will become our network DNS server

Because both of these containers live in a pod, we can share address space.
The pihole environment variable DNS points to which is the port we’ve setup Cloudflared to use.

kubectl apply -f pihole-deployment.yaml

If your deployment step was successful, pihole should be running

kubectl get pod -n pihole

The last step is to create a service to allow the outside world to interact/connect to our pihole pod. Pihole will be used as the DNS server for your network, so it’s important to use a static/fixed ip address. Select an available ip address in your metallb load balancer address space. Then edit this file and replace the xxx.xxx.xxx.xxx with the correct ip address.

kubectl apply -f pihole-service.yml

If the service installed successfully, you should be able to login to your pihole instance using the loadbalancer ip address you selected in the previous step. The default password is ‘nojunk’ (set in the pihole-deployment.yml file) http://xxx.xxx.xxx.xxx/admin