I’ve decided to move my DNS server from a VM in the physical ESXi, to some pods in my Raspberry Pi K3s cluster. If you have some Raspberry Pis laying around and want to setup a simple K8s cluster, checkout my guide: K3s on the Raspberry Pi
The below guide will assume that you’ve setup a kubernetes cluster and have some external Load Balancer configured. In my case, I’m using MetalLB.
Configure the Deployment and Service yaml
The below file will include our deployment using the cytopia/bind image, and an L4 service type LoadBalancer. See additional details on cytopia
It will create 2 services, 1 for UDP 53 and another for TCP 53.
Specify all the A records and the PTR (reverse dns) records that you want added.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | --- apiVersion: apps/v1 kind: Deployment metadata: name: dns spec: selector: matchLabels: app: dns replicas: 3 template: metadata: labels: app: dns spec: containers: - name : dns image: cytopia/bind ports: - containerPort : 53 env: - name : DNS_PTR value: 192.168.3.4=esxi2.home.lab , 192.168.3.5=esxi1.home.lab - name : DNS_A value: esxi2=192.168.3.4 , esxi1=192.168.3.5 - name : ALLOW_QUERY value: any - name : DOCKER_LOGS value: '1' --- apiVersion: v1 kind: Service metadata: name: dns-service-udp annotations: metallb.universe.tf/address-pool : dnspool metallb.universe.tf/allow-shared-ip : "dnskey" spec: selector: app: dns ports: - protocol : UDP port: 53 targetPort: 53 type: LoadBalancer loadBalancerIP: 192.168.3.199 --- apiVersion: v1 kind: Service metadata: name: dns-service-tcp annotations: metallb.universe.tf/address-pool : dnspool metallb.universe.tf/allow-shared-ip : "dnskey" spec: selector: app: dns ports: - protocol : TCP port: 53 targetPort: 53 type: LoadBalancer loadBalancerIP: 192.168.3.199 |
Apply the DNS yaml
1 2 | # Apply the yaml kubectl apply -f dns.yaml |

Testing
1 2 3 4 5 6 7 8 9 10 11 | # Kubernetes Objects kubectl get pods kubectl get service # Test DNS # +short just returns the response # +vc tests TCP, the default is UDP dig @192.168.3.199 esxi1.home.lab +short dig @192.168.3.199 esxi1.home.lab +short +vc dig @192.168.3.199 -x 192.168.3.5 +short dig @192.168.3.199 -x 192.168.3.5 +short +vc |


Extra
It’s rather annoying to have to add each DNS entry into that list, especially to duplicate it again with the PTR records. Having to do that manually might take minutes..
So I have a simple python script that will take a list of fqdn and IP addresses, and convert them into the appropriate yaml format
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #!/usr/bin/python3 import yaml dns_list = { "esxi2.home.lab" : "192.168.3.4" , "esxi1.home.lab" : "192.168.3.5" , "ns1.home.lab" : "192.168.3.6" } def Arecords(dnslist): DNS_A = [] for dns in dnslist: DNS_A.append(dns + "=" + dnslist[dns]) return ( ', ' .join(DNS_A)) def PTRrecords(dnslist): DNS_PTR = [] for dns in dnslist: DNS_PTR.append(dnslist[dns] + "=" + dns) return ( ', ' .join(DNS_PTR)) if __name__ = = '__main__' : json = [] json.append({ "name" : "DNS_PTR" , "value" : PTRrecords(dns_list)}) json.append({ "name" : "DNS_A" , "value" : Arecords(dns_list)}) json.append({ "name" : "ALLOW_QUERY" , "value" : "any" }) json.append({ "name" : "DOCKER_LOGS" , "value" : "1" }) print (yaml.dump(json,sort_keys = False )) |