Multi-Pi-hole Support
Relevant source files
The following files were used as context for generating this wiki page:The Overlord Network Kill Switch is designed to manage and synchronize DNS filtering rules across multiple Pi-hole instances. This ensures consistent behavior for networks that utilize more than one Pi-hole for redundancy or load balancing. The system achieves this by iterating through a configured list of Pi-hole servers and applying the same commands to each one, effectively treating them as a single logical unit.
This functionality is built into the core Pi-hole management classes, allowing for both granular domain-list control and global DNS blocking to be applied universally. The system also includes logic to detect and report on inconsistent states between the managed Pi-hole instances.
Sources: README.md, lib/pihole/base.py, lib/pihole/alldns.py
Configuration
To enable multi-Pi-hole support, you must provide a list of Pi-hole IP addresses. This can be done via an environment variable or a configuration file. The application parses this list and uses it to establish connections to each Pi-hole instance.
Sources: README.md, cgi-bin/controller.py:90-95
Configuration Parameters
| Parameter | Location | Format | Example | Description |
|---|---|---|---|---|
REMOTE_PI_LIST | Environment Variable | Space-separated string of IPs | "192.168.1.10 192.168.1.11" | A list of all Pi-hole server IPs to manage. |
remote_pi_list | etc/config.ini | Space-separated string of IPs | 192.168.1.10 192.168.1.11 | A list of all Pi-hole server IPs to manage, under the [general] section. |
REMOTE_PI_PASSWORD | Environment Variable / config.ini | String | your_password | The admin password, which must be the same for all managed Pi-hole instances. |
Sources: README.md:32, cgi-bin/controller.py:90-101, lib/pihole/base.py:22
The configuration is loaded during application startup by the init_config function.
# cgi-bin/controller.py:90-95
app_config["remote_pi_list"] = config.get(
"general",
"remote_pi_list",
fallback=os.environ.get("REMOTE_PI_LIST"),
).split(sep=" ")
Sources: cgi-bin/controller.py:90-95
Core Architecture
The multi-Pi-hole functionality is implemented through a class hierarchy where a base handler manages connections, and specialized subclasses implement specific logic for domain and global DNS control.
Class Structure
The BaseHTTPHandler class is the foundation for all Pi-hole interactions. It is responsible for storing the list of Pi-hole IPs (piList) and managing a dictionary of active sessions (sessions) to each Pi-hole. The PiHoleOverlord and MasterEnabler classes inherit from this base class and leverage the shared session management to execute commands across all configured instances.
The following diagram illustrates the relationship between these classes.
Connection Management
The BaseHTTPHandler.first_connect() method establishes and stores sessions for all Pi-holes. It iterates through the piList and creates a PiHole6Client object for each IP address, storing it in the self.sessions dictionary with the IP as the key. This process is triggered on the first API call if a connection has not already been established.
Sources: lib/pihole/base.py:64-80
The following sequence diagram shows the connection flow.
Synchronization Logic
Once connections are established, the handler classes iterate over the sessions dictionary to perform actions, ensuring that each command is sent to every configured Pi-hole.
Domain Blocking (PiHoleOverlord)
The PiHoleOverlord class manages domain-level blocklists and allowlists.
- Status Check (
get): To check if a domain block is active, thegetmethod queries the first Pi-hole in the list. It assumes consistency but is designed to check all if needed. If a domain is found to be disabled on any Pi-hole, it immediately returns afalsestatus for the entire group. - State Change (
post): When enabling or disabling a domain block, thepostmethod iterates through every session and sends the correspondingadd_domainordelete_domaincommand to each Pi-hole.
Sources: lib/pihole/pihole.py:72-100, 102-136
This flowchart shows the process for changing a domain block's state.
Sources: lib/pihole/pihole.py:102-136
Global DNS Control (MasterEnabler)
The MasterEnabler class handles enabling or disabling DNS blocking globally across all Pi-holes.
- Status Check (
get): Thegetmethod queries the blocking status of each Pi-hole individually. It collects the status from all instances and compares them. If any inconsistency is found (e.g., one is enabled while another is disabled), it logs a warning and returns anunknownstatus. - State Change (
flip_mode): This method sends theset_blocking_statuscommand to every Pi-hole. After sending the commands, it re-fetches the status from all devices to verify that they are in a consistent state.
Sources: lib/pihole/alldns.py:42-63, 91-108
This flowchart depicts the global DNS status check logic.
Sources: lib/pihole/alldns.py:91-108
API Endpoints
The multi-Pi-hole functionality is exposed through the /pihole and /alldns API endpoints. These routes are initialized in controller.py and defined in pihole_router.py.
Sources: cgi-bin/controller.py:207-209, lib/pihole/pihole_router.py
| Method | Endpoint | Handler Class | Description |
|---|---|---|---|
GET | /pihole/status/{domain_block} | PiHoleOverlord | Checks if a specific domain block group is enabled. |
POST | /pihole/enable/{domain_block} | PiHoleOverlord | Enables a domain block group on all Pi-holes. |
POST | /pihole/disable/{domain_block} | PiHoleOverlord | Disables a domain block group on all Pi-holes. |
GET | /alldns/ | MasterEnabler | Gets the global DNS blocking status across all Pi-holes. |
POST | /alldns/ | MasterEnabler | Disables global DNS blocking on all Pi-holes, with an optional timer. |
DELETE | /alldns/ | MasterEnabler | Enables global DNS blocking on all Pi-holes. |
Sources: lib/pihole/pihole_router.py:49-70, 79-93
Summary
The multi-Pi-hole support is a core feature that provides centralized control over a distributed set of Pi-hole DNS servers. By abstracting the interactions into handler classes that operate on a collection of sessions, the system ensures that DNS filtering rules are applied and synchronized consistently. The architecture is robust, handling connection management and state verification to provide a unified management experience for multiple Pi-hole instances.
Sources: README.md, lib/pihole/base.py