Deploying Highly Available Pi-hole v6 on K3s
Introduction
This guide walks through the installation of Pi-hole v6 on a Kubernetes (K3s) cluster. Unlike a traditional standalone install, this setup leverages Cloud-Native principles to ensure:
-
High Availability: Using Kube-Vip and MetalLB to provide a persistent "Virtual IP" (VIP) for DNS queries.
-
Data Persistence: Utilizing Longhorn distributed storage so your blocklists and logs survive node failures.
-
Secure Access: Using Traefik and Cert-Manager (with Let's Encrypt) to provide HTTPS access to the web dashboard.
Prerequisites
Before applying the Helm chart, ensure your environment meets the following requirements:
Cluster Components
-
A functional Kubernetes cluster, with a Load Balancer like Kube-Vip or MetalLB. See High-Availability k3s Cluster Guide with k3sup and kube-vip
-
Storage Class: Longhorn installed and set as a storage provider. See Setting up Distributed Storage System in k3s with Longhorn
-
An Ingress Controller like Traefik (default on K3s) installed and listening on ports 80/443. Configured Cert-Manager with a
ClusterIssuer(e.g., Let's Encrypt via Cloudflare DNS-01 challenge). See Configuring Ingress and TLS Certificates with Traefik and Cert-Manager
Networking
-
A reserved Static IP for your DNS (e.g.,
192.168.100.102). -
A registered domain or local DNS record pointing to your Ingress (e.g.,
pihole.homelab.penguincave.link).
Configure and Installation
Web UI Password
To have a password authentication upon installation, it's best to have a secret created so we don't have to expose it in our helm values file.
kubectl create secret generic pihole-password \
--from-literal=password='your-secure-password' \
-n pihole
Helm Values
The easiest way to deploy apps in Kubernetes is by Helm charts. Create a file named pihole-values.yaml. This configuration is specifically tuned for Pi-hole v6 and Kube-Vip.
# Your web UI admin password
admin:
existingSecret: pihole-password
passowrdKey: password
# Pi-hole v6 FTL Configuration
extraEnvVars:
# Force Pi-hole to respond to the K8s gateway IP
FTLCONF_dns_listeningMode: "all"
# These help the engine recognize the LoadBalancer IP
FTLCONF_dns_reply_host_IPv4: "192.168.100.102"
FTLCONF_dns_reply_host_force4: "true"
# Optional, if you need API access
FTLCONF_webserver_api: "true"
# Persistence using Longhorn
persistentVolumeClaim:
enabled: true
storageClass: "longhorn"
size: 5Gi
# Service configuration
serviceDns:
enabled: true
type: LoadBalancer
loadBalancerIP: "192.168.100.102"
# CHANGE THIS: Local -> Cluster
# This ensures any node can receive the DNS query
externalTrafficPolicy: Local
annotations:
kube-vip.io/loadbalancerIPs: "192.168.100.102"
kube-vip.io/allow-shared-ip: "pihole-dns"
# Security Context for performance
containerSecurityContext:
capabilities:
add:
- SYS_NICE
- NET_ADMIN
# Ingress configuration for Traefik + Cert-Manager
ingress:
enabled: true
ingressClassName: traefik
networkPolicy:
enabled: false
annotations:
# Adding this back fixes the CLASS <none> visual bug in kubectl
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: "letsencrypt-cloudflare"
traefik.ingress.kubernetes.io/router.entrypoints: websecure
hosts:
- pihole.homelab.penguincave.link
tls:
- secretName: pihole-cert-secret
hosts:
- pihole.homelab.penguincave.link
# DNS Upstreams
DNS1: "1.1.1.1"
DNS2: "8.8.8.8"
# Resource optimization for Longhorn/K3s
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
Installation
Add the Pi-hole Helm repository and install the chart:
# Add repo
helm repo add mojo2600 https://mojo2600.github.io/pihole-kubernetes/
helm repo update
# Install
helm install pihole mojo2600/pihole -n pihole -f pihole-values.yaml
Verification
Once the pods are running (kubectl get pods -n pihole), verify the setup from a remote machine:
-
Test DNS Resolution:
nslookup google.com <serviceDns.loadBalancerIP>, in this case 192.168.100.102 -
Verify Web UI: Navigate to your ingress hostname, in this case
https://pihole.homelab.penguincave.link/adminand log in with the password you set in your values file.