Hi there,
Today, I’d like to share a simple way to manage custom DNS entries on multiple Pi-hole servers using Ansible. I’m currently running three Pi-hole servers as Docker containers – two in the cloud and one on-premises – to provide redundant DNS for my Netbird network.
However, having to update all three servers manually can get tedious very quickly.
So, I will be using Ansible to make this way more efficient.
The setup is straightforward: we’ll replace the “custom.list” file in the Pi-hole config folder.
I assume you have already set up SSH keys and hosts configuration for your servers.
Creating the Ansible role
For this, I’ll create a role, since I prefer the flexibility and I am more used to this kind of set up.
We create a folder and the required subfolders.
ansible :: ~ » cd Ansible/roles
# Create folders
ansible :: roles » mkdir -p ansible-role-pihole/tasks/copy-config ansible-role-pihole/files/config/pihole
# List folders
ansible :: roles » tree ansible-role-pihole
ansible-role-pihole
├── files
│ └── config
│ └── pihole
├── tasks
│ ├── copy-config
Next, we create the ansible role yml file we will use to execute the playbooks.
ansible :: roles » vim ansible-role-pihole.yml
---
- hosts: pihole
gather_facts: true
become: true
roles:
- ansible-role-pihole
Create a task to copy the “custom.list” file to the Pi-hole servers and place this task in the “tasks/copy-config” folder.
Update the path to match your Pi-hole configuration. In my case it would be “/etc/pihole/pihole”, but that’s not the default.
ansible :: roles » cd ansible-role-pihole/tasks/copy-config
ansible :: copy-config » vim copy-config-pihole-custom-dns.yml
---
- name: pihole custom dns configuration
copy:
src: files/config/pihole/custom.list
dest: <path-to-pihole-folder>/custom.list
owner: root
mode: 0644
backup: yes
Create a “main.yml” file in the tasks folder “ansible-role-pihole”.
ansible :: roles » cd ansible-role-pihole/tasks
ansible :: tasks » vim main.yml
- import_tasks: copy-config/copy-config-pihole-custom-dns.yml
tags: custom-dns
The last file we need, is the “custom.list”. Create it in the “files/config/pihole” folder. The content is a simple list of IP address and hostname.
ansible :: roles » cd ansible-role-pihole/files/config/pihole/
ansible :: pihole » vim custom.list
10.10.0.103 ipa.example.local
10.10.0.108 file.exmaple.local
Alright. Just a quick look into the ansible hosts file.
ansible :: Ansible » vim hosts
all:
children:
pihole:
hosts:
pihole-01:
ansible_host: <ip-address>
pihole-02:
ansible_host: <ip-address>
pihole-03:
ansible_host: <ip-address>
Let’s take another look at the file structure.
ansible :: Ansible » tree roles/ansible-role-pihole
roles/ansible-role-pihole
├── files
│ └── config
│ └── pihole
│ └── custom.list
├── tasks
│ ├── copy-config
│ │ └── copy-config-pihole-custom-dns.yml
│ └── main.yml
Ok. Now execute the playbook.
ansible :: Ansible » ansible-playbook roles/ansible-role-pihole.yml -i hosts
PLAY [pihole] ***********************************************************************************************************
TASK [Gathering Facts] **************************************************************************************************
ok: [pihole-01]
ok: [pihole-02]
ok: [pihole-03]
TASK [ansible-role-pihole : pihole custom dns configuration] ************************************************************
changed: [pihole-01]
changed: [pihole-02]
changed: [pihole-03]
PLAY RECAP **************************************************************************************************************
pihole-01 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
pihole-02 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
pihole-03 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And that is it.
(Update)
I am going to restart the DNS resolver service after the custom.list has been replaced, since it takes a while until the resolver picks it up.
Add the following (marked in orange) to the “copy-config-pihole-custom-dns.yml” file.
---
- name: pihole custom dns configuration
copy:
src: files/config/pihole/custom.list
dest: <path-to-pihole-folder>/custom.list
owner: root
mode: 0644
backup: yes
register: pihole_custom_dns
Now create a new yml file and type in the following. This will execute the “pihole restartdns” command within the docker container. Make sure the container name fits your environment.
- name: Restart PiHole DNS Resolver
community.docker.docker_container_exec:
container: <container-name>
command: /usr/local/bin/pihole restartdns
chdir: /
when: pihole_custom_dns.changed
Once that’s done, add the task to the “main.yml” file.
- import_tasks: copy-config/copy-config-pihole-custom-dns.yml
tags: custom-dns
- import_tasks: docker-exec.yml
tags: pihole-docker-exec
(Update)
Of course, we could manage much more than that. For instance, we could update the upstream DNS servers or modify specific settings like DNSSEC.