Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/damianiglesias/pihole-ubuntu-deploy/llms.txt

Use this file to discover all available pages before exploring further.

Pi-hole Ubuntu Deploy has been tested in two VM network configurations. The right choice depends on whether you want Pi-hole to serve your entire LAN or only the host machine.
VPN and NAT configurations are not covered in v1 of the project. The author notes that bridged networking is the simplest approach for full LAN coverage, and more complex topologies will be addressed in a future version.

Bash Script Deployment

When using deploy.sh to install Pi-hole directly on the OS, the VM’s network adapter mode determines which devices can reach the Pi-hole DNS server.
In Bridged mode, the VM is treated as a first-class device on your physical LAN. It receives its own IP address from your router (or a static IP assigned by deploy.sh), and any device on the network can point its DNS to Pi-hole.How it works:
[Router / DHCP]
      |
──────┬──────────────────────
      │           LAN (192.168.x.x)
  [VM / Pi-hole]   [PC]   [Phone]   [TV]
  192.168.1.50
Advantages:
  • All LAN devices can use Pi-hole as their DNS server
  • No port forwarding or VPN required
  • Static IP assigned directly to the VM interface via Netplan
  • Recommended for production deployments
Setup:
  1. Set your VM’s network adapter to Bridged in VirtualBox/VMware
  2. Run sudo ./deploy.sh and select Static IP when prompted
  3. Set Pi-hole’s IP (192.168.1.x) as the DNS server in your router’s DHCP settings
After enabling a static IP via deploy.sh, point your router’s primary DNS server to the Pi-hole IP. All DHCP clients will then automatically use Pi-hole for ad blocking.

Ansible / Docker Deployment

When using the Ansible playbook, Pi-hole and Unbound run as Docker containers inside a custom bridge network. The host OS acts as the gateway, publishing container ports to the physical network interface.

Docker Network: pihole_net

The playbook creates a dedicated Docker bridge network named pihole_net with a /16 subnet:
Docker Bridge: pihole_net (172.20.0.0/16)
┌─────────────────────────────────────────┐
│                                         │
│   [pihole container]  172.20.0.2        │
│   [unbound container] 172.20.0.5        │
│                                         │
└──────────────┬──────────────────────────┘
               │ Published ports
         [Host OS / VM]
         Port 53  → pihole:53
         Port 80  → pihole:80
         Port 5335 → unbound:5335
ContainerImageFixed IPRole
piholepihole/pihole:latest172.20.0.2DNS sinkhole + web admin
unboundmvance/unbound:latest172.20.0.5Recursive DNS resolver

DNS Forwarding Between Containers

Pi-hole is configured with PIHOLE_DNS_1: '172.20.0.5#5335' — it forwards all upstream queries to the Unbound container on port 5335, keeping both containers within the private pihole_net network and avoiding any external dependency for recursion.

Port Reference

All ports used by Pi-hole Ubuntu Deploy — both for the direct OS install and the Docker method.
PortProtocolServiceDirection
22TCPSSH accessInbound
53TCP + UDPDNS queriesInbound
80TCPPi-hole web adminInbound
5335UDPUnbound recursive resolver (if enabled)Internal
In the Ansible playbook, UFW only opens port 53 over TCP (task 9 loops over TCP only). UDP port 53 is handled by Docker’s port publishing, which bypasses UFW by default on Linux. For deploy.sh, ufw allow 53 opens both TCP and UDP.

DNS Query Flow

The resolution chain differs depending on whether Unbound is enabled.
DNS queries are resolved by an external upstream (Google or Cloudflare). Blocked domains return 0.0.0.0.
Client

  ▼  port 53
Pi-hole

  ├──► BLOCKED → returns 0.0.0.0 (ad/tracker domain)

  └──► Upstream DNS (8.8.8.8 or 1.1.1.1)

           └──► Returns IP to Pi-hole → Client
This is the default configuration after running deploy.sh without enabling Unbound.

Static IP and Netplan

A static IP is critical for a DNS server. If the Pi-hole machine’s IP changes (e.g., due to DHCP lease renewal), all devices on the network that point to Pi-hole as their DNS server will lose connectivity until the address is updated. deploy.sh handles this at Step 6 by writing a Netplan configuration that:
  1. Detects the current interface IP and default gateway automatically
  2. Locks that IP as static by disabling DHCP (dhcp4: false)
  3. Sets nameservers to 8.8.8.8 (external fallback) and 127.0.0.1 (Pi-hole itself)
  4. Backs up all existing Netplan YAML files to /etc/netplan/backup/ before writing
network:
  version: 2
  renderer: networkd
  ethernets:
    enp0s8:
      dhcp4: false
      addresses: [192.168.1.50/24]
      routes: [{to: default, via: 192.168.1.1}]
      nameservers: {addresses: [8.8.8.8, 127.0.0.1]}
The file is written with chmod 600 permissions as required by netplan apply.
After applying the static IP, update your router’s DNS server setting to point to the Pi-hole IP. This ensures all DHCP clients on the network automatically use Pi-hole for DNS without any per-device configuration.

Build docs developers (and LLMs) love