What is Pi-hole?
Pi-hole is a DNS sinkhole that blocks ads, trackers and malware domains for every device on your network, including smart TVs and phones where browser extensions cannot run. It is open source under the EUPL-1.2 license and includes a web dashboard with query logs, per-client statistics, blocklist management and an optional DHCP server.
Requirements
- Any always-on Linux machine on your home network. A Raspberry Pi, mini PC or VM with 512 MB of RAM is enough.
- Docker Engine and Docker Compose v2.
- A static IP on your LAN, set on the device or reserved in your router.
- Access to your router to change the DNS server handed out to clients.
Step 1: Prepare the server
This guide assumes Ubuntu 24.04 with Docker installed; if you need Docker, use the official install guide. Ubuntu's systemd-resolved already listens on port 53, so free it first:
sudo sed -r -i.orig 's/#?DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf
sudo sh -c 'rm /etc/resolv.conf && ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf'
sudo systemctl restart systemd-resolved
mkdir -p ~/pihole && cd ~/pihole
Step 2: Create the Docker Compose file
Pi-hole v6 is configured through FTLCONF_ environment variables, which map directly to settings in pihole.toml. Save this as docker-compose.yml:
services:
pihole:
container_name: pihole
image: pihole/pihole:2026.09.0
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80/tcp"
# Uncomment if Pi-hole will be your DHCP server:
# - "67:67/udp"
environment:
TZ: "Europe/London"
FTLCONF_webserver_api_password: "CHANGE_ME"
FTLCONF_dns_listeningMode: "ALL"
FTLCONF_dns_upstreams: "1.1.1.1;9.9.9.9"
volumes:
- ./etc-pihole:/etc/pihole
cap_add:
- NET_ADMIN # only required if Pi-hole is your DHCP server
- SYS_NICE # optional: gives FTL more CPU priority
Replace CHANGE_ME with a strong password, for example from openssl rand -hex 32. FTLCONF_dns_listeningMode: "ALL" is required on Docker's default bridge network, because Pi-hole sees queries arriving from the Docker gateway rather than your LAN. This guide maps the web interface to port 8080 so port 80 stays free for a reverse proxy. Pin the image to a release from the Docker Pi-hole releases, since latest can jump to a new version without warning.
Never publish port 53 to the internet on a VPS. Open resolvers are abused for DDoS amplification. Use a firewall at your provider, or bind to your LAN address, for example "192.168.1.10:53:53/udp".
Step 3: Start and open the app
docker compose up -d
dig @127.0.0.1 example.com +short
If dig returns an IP address, DNS works. Open http://SERVER_IP:8080/admin and sign in with your password. Then point your network at Pi-hole: in your router's DHCP settings, set the DNS server to the Pi-hole's LAN IP and remove any secondary public DNS, otherwise devices bypass the blocking. Reconnect a device and watch its queries appear in the dashboard.
Step 4: Put it behind HTTPS
On a LAN, HTTPS for the admin page is optional. If you run a reverse proxy, Caddy can serve it with a certificate for an internal name:
pihole.home.example.com {
redir / /admin/ 302
reverse_proxy 127.0.0.1:8080
}
For a public certificate on an internal-only name, use Caddy's DNS challenge, or Nginx Proxy Manager with a DNS provider. Do not open the admin page or DNS port to the internet.
Backups and upgrades
All configuration lives in ./etc-pihole: pihole.toml, the gravity database with your lists, and local DNS records. Pi-hole also exports a Teleporter archive under Settings, Teleporter, which restores everything into a fresh install. Download one after each significant change.
Upgrades happen by replacing the container. pihole -up does not work inside Docker. Update the image tag, then:
docker compose pull && docker compose up -d
Troubleshooting
- "Address already in use" on port 53:
systemd-resolvedor another DNS server is still running. Repeat Step 1 and check withsudo ss -lunpt | grep :53. - Dashboard shows queries only from one IP: all traffic is coming through the Docker gateway. That is expected on bridge networking; use
network_mode: hostif you need per-client statistics. - Some ads still appear: devices may use hard-coded DNS or DNS-over-HTTPS. Block outbound port 53 to other servers on your router where possible.
- Login fails after an upgrade: check the password in the Compose file. If it was unset, find the generated one in
docker compose logs pihole.
Next steps
Add blocklists under Lists, define local DNS records for your services, enable conditional forwarding for hostname resolution, and run a second Pi-hole for redundancy.
Spotted something out of date? Tell us and we will update the guide.