Skip to content

Instantly share code, notes, and snippets.

@ascopes
Last active August 22, 2025 11:10
Show Gist options
  • Select an option

  • Save ascopes/b602c6f03b75e611c9a5ece7d5293b62 to your computer and use it in GitHub Desktop.

Select an option

Save ascopes/b602c6f03b75e611c9a5ece7d5293b62 to your computer and use it in GitHub Desktop.
pihole config for k3s (/var/lib/rancher/k3s/server/manifests/pihole.yaml)

PiHole configuration for Raspberry Pi running k3s.

Requirements

  • network router should be pointing to the IP address of the k3s cluster for DNS.
  • k3s cluster must have a static IP assigned to it via DHCP by the network router.
  • k3s is required, as this assumes we're using traefik as the reverse proxy.
  • the host running the k3s control plane node should have the resolv.conf in this gist copied to /etc/resolv.conf (if you use systemd-resolved, good luck, you'll have to find a way to overwrite this so systemd doesn't just blindly revert it for you).

What this setup does

  • installs pihole (obviously!)
  • creates a synthetic A/AAAA DNS entry for the k3s cluster itself.
  • creates synthetic CNAME DNS entry for the pihole server. This is needed as pihole v6 cannot correctly set up custom path routing. We can get the admin UI login page to work if we override several variables and inject a path-stripping traefik middleware, but the cookies served by pihole do not respect the new paths (since the cookie is bound to a specific path), and we have no way of fixing this without custom traefik plugins from thirdparty sources, which is a massive pain in the arse to get working.
  • exposes the pihole server on pihole.$$PIHOLE_HOST$$ as the pihole interface.
  • configures DoH to go via cloudflare, using a DoH sidecar container to ensure external DNS traffic is encrypted.
  • forces the pihole deployment to run on the control plane node, since the control plane itself will rely on pihole to resolve DNS entries for external traffic, including K8S image resolution.

Where do I put things?

  • The pihole.yaml should be placed in /var/lib/rancher/k3s/server/manifests/pihole.yaml, where K3S will pick it up automatically and apply it.
  • resolv.conf goes in /etc/resolv.conf

Useful links

apiVersion: v1
kind: Namespace
metadata:
name: pihole
---
apiVersion: v1
kind: Secret
metadata:
name: pihole-web-admin
namespace: pihole
type: Opaque
data:
password: base64encodedpasswordhere==
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: pihole
namespace: pihole
spec:
repo: https://mojo2600.github.io/pihole-kubernetes/
chart: pihole
version: 2.31.0
targetNamespace: pihole
valuesContent: |-
###############
# Block lists #
###############
# Note that you may have to delete the PVC for PiHole and delete the pod for updates to these
# lists to take effect. Generally, it is easier to update this list for later and manually add
# the domains to the pihole in the meantime via the web UI.
adlists:
# EasyList (adguard uses this as a base for their main rules)
- https://v.firebog.net/hosts/Easylist.txt
################################################
### https://github.com/hagezi/dns-blocklists ###
################################################
# Generic domain block list.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/pro.txt
# Various ads.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/popupads.txt
# Threat feeds
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/domains/tif.txt
# Prevents access to external DNS services over DoH (Firefox and Opera on Android do this by default).
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/doh-vpn-proxy-bypass.txt
# Prevents access to malicious DDNS services.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/dyndns.txt
# Windows and Microsoft Office trackers.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/native.winoffice.txt
# Apple trackers.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/native.apple.txt
# Amazon trackers.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/native.amazon.txt
# Samsung trackers.
- https://raw.githubusercontent.com/hagezi/dns-blocklists/main/adblock/native.samsung.txt
##################################################################################################
### https://discourse.pi-hole.net/t/adlist-sites-to-use-march-2024-what-is-recommended/69082/2 ###
##################################################################################################
# Ads and trackers.
- https://www.github.developerdan.com/hosts/lists/ads-and-tracking-extended.txt
# Known phishing domains.
- https://phishing.army/download/phishing_army_blocklist.txt
#################################################
### https://github.com/xRuffKez/NRD/tree/main ###
#################################################
# Domains that were registered in the past 14 days.
- https://raw.githubusercontent.com/xRuffKez/NRD/refs/heads/main/lists/14-day-mini/adblock/nrd-14day-mini_adblock.txt
whitelist:
# Used for Opera's free VPN.
- api2.sec-tunnel.com
############
# Settings #
############
admin:
existingSecret: pihole-web-admin
passwordKey: password
DNS1: 1.1.1.2 # cloudflare with malware blocking
DNS2: 9.9.9.9 # quad9
dnsmasq:
customDnsEntries:
# Make this server visible on our network with a .lan TLD
- address=/$$PIHOLE_HOST$$.lan/$$PIHOLE_HOST_IP$$
# We have to hack in this custom host as pihole doesn't let us override the base admin
# path properly without totally breaking cookies, which means it won't work properly.
# Instead, it forces us to use a custom domain. Sigh.
- address=/pihole.$$PIHOLE_HOST$$.lan/$$PIHOLE_HOST_IP$$
doh:
enabled: true
envVars:
DOH_UPSTREAM: >-
# 1.1.1.2 and 1.0.0.2 = Cloudflare static anycast with malware blocking
https://1.1.1.2/dns-query,
https://1.0.0.2/dns-query,
https://dns.quad9.net/dns-query
extraEnvVars:
# FTLCONF keys are "documented" at https://github.com/pi-hole/FTL/blob/master/src/api/docs/content/specs/config.yaml
TAIL_FTL_LOG: 1 # 1 to enable FTL logs, 0 to disable.
ftl:
# Disable local network check for dnsmasq. By default it will kick any traffic coming
# from more than one hop away, but we're tunneling traffic through a load balancer,
# so we'll get more than one hop from our local network anyway.
dns_listeningMode: all
image:
tag: 2025.08.0
ingress:
enabled: true
hosts:
- pihole.$$PIHOLE_HOST$$.lan
maxUnavailable: 0
maxSurge: 1
nodeSelector:
# Must schedule on a specific node in multi-node setups since only a
# single known IP address is exposed to the network. If we used DHCP here
# then we would still have the same issue.
# Note that this must NOT have a TLD... it must exactly match the system hostname.
kubernetes.io/hostname: $$PIHOLE_HOST$$
persistentVolumeClaim:
enabled: true
size: 1Gi
podDnsConfig:
nameservers:
# Don't use the default nameservers, it will just circle back to us again
# and crash everything.
- 1.1.1.1
- 9.9.9.9
- 8.8.8.8
probes:
# Note that this will become the default in the next release of the pihole-kubernetes chart
# after tag pihole-2.31.0 on GitHub. Right now it defaults to an httpGet check instead which
# will always fail with a 404 since we've adjusted the base path.
_: &piholeProbe
type: command
command:
- /bin/sh
- -c
- "curl --silent http://localhost/api/info/login | jq 'if (.dns | not) then halt_error(1) end'"
liveness:
<<: *piholeProbe
readiness:
<<: *piholeProbe
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
serviceDhcp:
enabled: false
serviceDns:
loadBalancerIP: $$PIHOLE_HOST_IP$$
mixedService: true
type: LoadBalancer
virtualHost: pihole.$$PIHOLE_HOST$$.lan
# Contents of /etc/resolv.conf on the host.
#
# We use the default gateway which points back to the pihole
# by default on the host resolver such that the entire host can
# utilise the pihole for resolution.
# If the pihole has not yet started, this will not work, and we need
# this to perform tasks like updating k8s, the OS, or pulling a newer
# copy of the pihole helm chart or container images. Thus, fall back
# to a public server.
nameserver 10.0.0.0 # default gateway here, should circle back to pihole
# Uncomment this when reinstalling pihole with no existing deployment. You might have
# to restart coredns first and comment out the other entries.
#nameserver 1.1.1.1

Comments are disabled for this gist.