pihole-kubernetes

repository·main·Indexed 20 days ago

https://github.com/mojo2600/pihole-kubernetes

A Helm chart and sample deployment for running Pi-hole on a Kubernetes cluster. It is specifically designed for on-premise environments using MetalLB for load balancing and Rook for storage. Features include support for DNS over HTTPS (DoH) via cloudflared, customizable dnsmasq settings, and integration with ExternalDNS. The chart provides dedicated configuration for DNS, DHCP, and Web services, including options for Ingress, ClusterIP, and LoadBalancer exposure.

Tokens
7.6K
Snippets
19
Records
27
Agent score
68%

What's inside pihole-kubernetes

  1. Preserve client IP addresses in PiHole

    main

    By default, Kubernetes services may mask the original client IP address. To ensure the PiHole pod receives the actual client IP address (which is necessary for accurate logging and filtering in the PiHole dashboard), set the spec.externalTrafficPolicy to Local in your service definitions instead of the default Cluster policy.

    spec:
      externalTrafficPolicy: Local
  2. Configure Pi-hole whitelists and blacklists on startup

    main

    You can configure Pi-hole to automatically apply whitelists and blacklists during the container startup process. This approach allows the container to be fully configured and ready for use immediately upon deployment, eliminating the need for manual post-installation configuration.

    Commonly used sources for these lists include:

  3. Create a mirrored secret for ExternalDNS using Reflector

    main

    Because Kubernetes secretRef can only reference secrets within the same namespace, use Reflector to mirror the Pi-hole password secret into the external-dns namespace.

    1. Add reflection annotations to the original Pi-hole admin configuration (see admin.annotations).
    2. Create a target Secret in the external-dns namespace with the reflector.v1.k8s.emberstack.com/reflects annotation pointing to the source namespace/secret-name.
    apiVersion: v1
    kind: Secret
    metadata:
      # Must match the secretRef used in the ExternalDNS deployment
      name: pihole-password
      # Must be the namespace where ExternalDNS is running
      namespace: external-dns
      annotations:
        # Format: 'source-namespace/source-secret-name'
        reflector.v1.k8s.emberstack.com/reflects: "pihole/pihole-password"
    data: {}
  4. Integrate ExternalDNS with Pi-hole

    main

    You can use ExternalDNS (v0.14+) to automatically expose Ingress hostnames to your Pi-hole Local DNS configuration.

    When configuring the external-dns deployment, ensure you include the following:

    • --provider=pihole
    • --pihole-server: The internal service address of your Pi-hole web server (e.g., http://pihole-web.pihole.svc.cluster.local).
    • --policy=upsert-only: IMPORTANT. Use this policy if you have manual records in Pi-hole to prevent ExternalDNS from deleting them.
    • --registry=noop: Prevents warning logs when ExternalDNS attempts to create TXT records (since Pi-hole only supports A/CNAME records).
    • envFrom.secretRef.name: The name of the secret containing the Pi-hole password.
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: external-dns
    spec:
      template:
        spec:
          containers:
          - name: external-dns
            image: registry.k8s.io/external-dns/external-dns:v0.14.0
            envFrom:
              - secretRef:
                  name: pihole-password
            args:
              - --source=service
              - --source=ingress
              - --registry=noop
              - --policy=upsert-only
              - --provider=pihole
              - --pihole-server=http://pihole-web.pihole.svc.cluster.local
  5. Upgrade from 1.8.22 to 2.0.0

    main

    Version 2.0.0 introduces a breaking change where the DHCP service is split into its own resource, managed under the serviceDhcp configuration key.

    • If you have NOT modified serviceDns: No action is required.
    • If you HAVE modified serviceDns: You must manually copy your existing serviceDns configuration block into a new serviceDhcp section in your values.yaml.
  6. Install PiHole on Kubernetes

    main

    To install the PiHole sample deployment, follow these steps:

    1. Setup Services: Apply the TCP and UDP service files to obtain an external IP address. In this sample, both services are configured to share the same IP.
      kubectl apply -f svc-pihole-tcp.yml
      kubectl apply -f svc-pihole-udp.yml
    2. Retrieve External IP: Check the services to find the EXTERNAL-IP assigned by your load balancer (e.g., MetalLB).
      kubectl get svc pihole-tcp
      kubectl get svc pihole-udp
    3. Configure Deployment: Update the deployment-pihole.yml file with the EXTERNAL-IP obtained in the previous step.
    4. Deploy Resources: Apply the remaining configuration files to complete the installation.
      kubectl apply -f pvc-pihole.yml
      kubectl apply -f secret-pihole-webpassword.yaml
      kubectl apply -f configmap-pihole-custom-dnsmasq.yml
      kubectl apply -f deployment-pihole.yml
    $ kubectl apply -f svc-pihole-tcp.yml
    $ kubectl apply -f svc-pihole-udp.yml
    $ kubectl apply -f pvc-pihole.yml
    $ kubectl apply -f secret-pihole-webpassword.yaml
    $ kubectl apply -f configmap-pihole-custom-dnsmasq.yml
    $ kubectl apply -f deployment-pihole.yml
  7. Request a specific IP address via MetalLB

    main

    If you are using MetalLB, you can ensure the PiHole services always receive a specific IP address by using a dedicated address pool and setting the spec.loadBalancerIP field in your service specification.

    Example configuration key: spec.loadBalancerIP: 192.168.178.254

    spec:
      loadBalancerIP: 192.168.178.254
  8. Add the mojo2600 Helm repository

    main

    To install the PiHole Helm charts on your Kubernetes cluster, add the mojo2600 repository to your local Helm configuration and update your local chart cache.

    helm repo add mojo2600 https://mojo2600.github.io/pihole-kubernetes/
    helm repo update
  9. Upgrade from older versions to 1.8.22

    main

    Version 1.8.22 splits the TCP and UDP services into dedicated serviceWeb and serviceDns configurations to improve compatibility with controllers like Traefik.

    Migration Steps: If you previously used serviceTCP and serviceUDP, you must update your values.yaml to use serviceWeb and serviceDns instead.

    Kubernetes API Compatibility: This version uses networking.k8s.io/v1 for Ingress. This requires Kubernetes version 1.19 or higher. If you are on an older version, you must manually modify ingress.yaml to use extensions/v1beta1 and update the backend definition structure.

    # Old configuration (pre-1.8.22)
    serviceTCP:
      loadBalancerIP: 192.168.178.252
      annotations:
        metallb.universe.tf/allow-shared-ip: pihole-svc
    
    serviceUDP:
      loadBalancerIP: 192.168.178.252
      annotations:
        metallb.universe.tf/allow-shared-ip: pihole-svc
    
    # New configuration (1.8.22+)
    serviceWeb:
      loadBalancerIP: 192.168.178.252
      annotations:
        metallb.universe.tf/allow-shared-ip: pihole-svc
    
    serviceDns:
      loadBalancerIP: 192.168.178.252
      annotations:
        metallb.universe.tf/allow-shared-ip: pihole-svc
  10. Configure PiHole service exposure

    main

    You can control how the PiHole service is exposed to your network using the following methods. These can be set via the --set flag during installation or by editing values.yaml:

    • Ingress: Requires an ingress controller to be installed in your cluster.
    • ClusterIP: Exposes the service on a cluster-internal IP. The service will only be reachable from within the cluster.
    • LoadBalancer: Exposes the service externally using a cloud provider's load balancer (or a local solution like MetalLB).