Manage PiHole Custom DNS entries with Ansible (Update)

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.

Leave a Reply