How to Set Up Pi-hole on Docker: A Beginner-Friendly Guide to Network-Wide Ad Blocking
Learn how to set up Pi-hole on Docker with Docker Compose, fix port 53 conflicts, point clients at Pi-hole, and verify network-wide ad blocking.
Author
David Okonkwo
FTC disclosure: This article contains affiliate links. If you purchase through these links, we may earn a commission at no additional cost to you.
If you've been meaning to clean up ads and trackers on your home network but every Pi-hole guide feels like it assumes you already understand DNS, Docker, and router settings, you're not the problem. Most tutorials jump straight into commands without explaining why each step matters. That is how people end up with a running container that still does not block anything.
This guide takes the slower, more useful path. You will set up Pi-hole on Docker, understand what each piece is doing, and finish with a setup you can actually verify.
Key Takeaways
- Pi-hole blocks ads and trackers by answering DNS requests before unwanted domains ever load.
- Docker makes Pi-hole easier to install, update, and back up because the application stays isolated from the host OS.
- The most common setup failures are port 53 conflicts, forgetting to point clients at Pi-hole, and not persisting
/etc/piholedata. - A Docker Compose setup is easier to read and maintain than a long
docker runcommand. - You do not need macvlan for a basic homelab install. Bridge mode works well for most beginners.
- The job is not done when the container starts. You need to test DNS resolution and confirm your devices are actually using Pi-hole.
Why this matters before you start
Pi-hole is often described as a network-wide ad blocker, but the more useful mental model is this: Pi-hole becomes the receptionist for your network's phonebook.
Every phone, laptop, smart TV, and container on your network asks DNS for directions before connecting to a website. If an ad or tracker domain gets blocked at that moment, the annoying part never loads in the first place. It is a lot like refusing junk mail at the mailbox instead of sorting it on your kitchen table.
Running Pi-hole in Docker matters for a different reason. It keeps the service portable. If you move from an old mini PC to a new one, or from Ubuntu to Debian, your Pi-hole data can come with you as mounted volumes instead of turning into a weekend rebuild.
What you'll need
Before you type anything, make sure you have:
- A Linux host with Docker and Docker Compose installed
- A static IP or DHCP reservation for the Docker host
- Access to your router or at least one client device so you can change DNS later
- A free port 53 on the host for DNS
- A browser on the same network for testing the Pi-hole dashboard
If your homelab host is still coming together, these existing guides will help:
- DHCP Explained for Homelabs: How IP Leases, Reservations, and Scope Planning Actually Work
- VLANs for Homelabs Explained: A Beginner Setup Guide That Will Not Wreck Your Weekend
- Pi-hole vs AdGuard Home: Which DNS Ad Blocker Should You Run in Your Homelab?
Recommended gear for a simple Pi-hole host
You do not need expensive hardware for Pi-hole, but stable low-power gear makes this much easier to live with.
- Raspberry Pi 5 kit - great if you want a tiny dedicated DNS box
- Beelink N100 mini PC - better if you also plan to run Docker services like Vaultwarden or Uptime Kuma
- APC Back-UPS 1500 - worth it if your DNS host also handles other critical homelab services
Step 1: Check whether port 53 is already in use
Why this matters
Pi-hole answers DNS on port 53. If another service is already listening there, your container can start and still fail to do the one job you actually care about.
On Ubuntu and Debian systems, systemd-resolved is the usual culprit. On NAS boxes and existing homelab hosts, it might be another DNS service, a reverse proxy bundle, or even an old Pi-hole install you forgot about.
Run this first:
sudo ss -lntup | grep ':53 '
If you see no output, that is good news. Port 53 is free.
If you do see something bound to port 53, identify it before you continue. On many Ubuntu systems, you will also want to check:
systemctl status systemd-resolved
If systemd-resolved is actively using port 53 and you want Pi-hole to own DNS on this host, stop and disable it:
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
Then re-run the socket check:
sudo ss -lntup | grep ':53 '
If you disable systemd-resolved, make sure your host still has working DNS for package installs. On some systems you may need to point /etc/resolv.conf at a real upstream resolver temporarily. The official references for this setup are the Pi-hole Docker docs and the Docker Compose docs.
Step 2: Create a directory for your Pi-hole stack
Why this matters
A named home for the compose file and persistent data keeps updates simple. You are building a service, not throwing commands at the shell and hoping your future self remembers what happened.
Create the project directory:
mkdir -p ~/containers/pihole
cd ~/containers/pihole
Create the persistent directories:
mkdir -p ./etc-pihole
mkdir -p ./etc-dnsmasq.d
Those folders are where your container will keep blocklists, settings, and DNS configuration between updates.
Step 3: Write the Docker Compose file
Why this matters
A compose file is easier to read than a single oversized docker run command. It also makes small changes safer. You can see your ports, volumes, and environment variables in one place instead of hunting through shell history.
Create compose.yaml:
cat > compose.yaml <<'YAML'
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
hostname: pihole
ports:
- "53:53/tcp"
- "53:53/udp"
- "8080:80/tcp"
environment:
TZ: "UTC"
FTLCONF_webserver_api_password: "change-this-to-a-strong-password"
FTLCONF_dns_listeningMode: "ALL"
volumes:
- "./etc-pihole:/etc/pihole"
- "./etc-dnsmasq.d:/etc/dnsmasq.d"
restart: unless-stopped
YAML
A few notes about that file:
- I used
8080:80for the web UI because many homelab hosts already use port 80 for a reverse proxy or another service. FTLCONF_dns_listeningMode: "ALL"is helpful when using Docker bridge networking.- The mounted directories are what keep your Pi-hole data from disappearing during upgrades.
If you want Pi-hole to act as a DHCP server too, you will need to expose UDP 67 and usually add NET_ADMIN. Most beginners should leave DHCP on the router for now. Running both DNS and DHCP on day one adds moving parts before you know the DNS layer works.
Step 4: Start the Pi-hole container
Why this matters
This is where Docker turns a configuration file into a real service. But do not stop at "container is up". That only proves Docker can start a process, not that your network is using it correctly.
Start Pi-hole:
docker compose up -d
Check that it is running:
docker compose ps
You should see the pihole service in a running state.
Then inspect the logs:
docker compose logs --tail=50
You are looking for obvious startup failures like:
- port already allocated
- permission issues on mounted folders
- password or environment variable parsing errors
- FTL or web interface startup failures
Step 5: Open the dashboard and sign in
Why this matters
The dashboard confirms the container is reachable over HTTP, but it does not yet prove your clients are using Pi-hole for DNS. Think of it like turning on a new router and seeing the LEDs light up. Useful, yes. Finished, no.
From a browser on your network, open:
http://YOUR-HOST-IP:8080/admin/
For example, if your Docker host is 192.168.1.20, visit:
http://192.168.1.20:8080/admin/
Sign in with the password you set in FTLCONF_webserver_api_password.
If the page does not load:
- Confirm the container is still running with
docker compose ps - Confirm the host is listening on port 8080:
sudo ss -lntup | grep ':8080 '
- Recheck host firewall rules if you use
ufw,firewalld, or a locked-down VM image
Step 6: Point a test device at Pi-hole first
Why this matters
Changing DNS at the router affects your whole network. That is fine when you are confident. It is not ideal when you are still proving the setup works.
I recommend testing with one laptop or desktop first. That gives you a controlled way to confirm blocking before you roll Pi-hole out everywhere.
Set the device's DNS server to the IP address of your Docker host. On Linux, NetworkManager, macOS system settings, or Windows adapter settings can all do this through the GUI. If you prefer Linux CLI with NetworkManager, the pattern looks like this:
nmcli connection show
nmcli connection modify "YOUR-CONNECTION-NAME" ipv4.dns "192.168.1.20"
nmcli connection modify "YOUR-CONNECTION-NAME" ipv4.ignore-auto-dns yes
nmcli connection up "YOUR-CONNECTION-NAME"
Replace 192.168.1.20 with your actual Docker host IP.
If you already understand your router well, you can later move the DNS setting there so every client gets Pi-hole automatically. That ties directly into the DHCP behavior explained in our DHCP guide.
Step 7: Verify DNS is actually flowing through Pi-hole
Why this matters
This is the step competitors gloss over. The dashboard existing is not proof. Queries hitting Pi-hole are proof.
From the test device, run one of these commands.
On Linux or macOS with dig installed:
dig @192.168.1.20 github.com
Or with nslookup:
nslookup github.com 192.168.1.20
If that returns a valid response, try an ad-heavy or known-blocked domain. You can also watch the live query log in the Pi-hole dashboard while making requests.
From the Docker host, you can also verify the container is healthy with:
docker exec -it pihole pihole status
And inspect recent logs again:
docker compose logs --tail=100
When your test device sends queries, you should see activity in the dashboard. That is the moment the setup becomes real.
Step 8: Move from one test client to the whole network
Why this matters
This is where Pi-hole becomes useful instead of merely interesting. If only one device uses it, you built a lab exercise. If the network uses it, you built an actual service.
You usually have two rollout options:
Option A: Set DNS on the router
This is the easiest way to move the whole network onto Pi-hole. Most routers let you set one or two DNS servers in the LAN or DHCP settings.
Use the Docker host IP as the primary DNS server. If your router insists on a secondary DNS, think carefully before adding a public resolver like 1.1.1.1 or 8.8.8.8. Clients may bypass Pi-hole whenever they choose the secondary resolver.
Option B: Set DNS per VLAN or per client group
This is better when your homelab is segmented. For example, you might want user devices to use Pi-hole, but lab servers or IoT gear to use different policies.
If that sounds like your network, read our VLANs guide next. Pi-hole works much better when you already know which devices belong in which lane.
Step 9: Make the setup easier to maintain
Why this matters
A service you cannot update safely is just future downtime with extra steps. DNS is one of those quiet dependencies that nobody notices until everything feels broken.
Update Pi-hole on Docker
When a new image is available, update like this:
cd ~/containers/pihole
docker compose pull
docker compose up -d
That refreshes the container while keeping your mounted data directories intact.
Back up your Pi-hole data
At minimum, back up these folders:
~/containers/pihole/etc-pihole
~/containers/pihole/etc-dnsmasq.d
A simple tarball works fine for a small homelab:
tar czf pihole-backup-$(date +%F).tar.gz ./etc-pihole ./etc-dnsmasq.d
If you are already backing up Docker services more broadly, our Vaultwarden on Docker guide follows the same persistence mindset.
Common mistakes
These are the mistakes I see beginners hit most often.
1. The container is running, but nothing is blocked
Usually this means your devices are still using the router, ISP, or public DNS directly. Recheck the client or router DNS settings and confirm query traffic appears in Pi-hole.
2. Port 53 is already in use
This is often systemd-resolved, another DNS service, or a previous Pi-hole install. Check sockets with:
sudo ss -lntup | grep ':53 '
3. You exposed port 80 on a busy homelab host
If you already run Nginx Proxy Manager, Caddy, or Traefik, mapping 80:80 can collide immediately. Using 8080:80 for the Pi-hole admin UI is a safer default for beginners.
4. You forgot persistence
If /etc/pihole is not mounted, updates can wipe out settings and blocklists. Docker without persistent storage is like writing notes on a whiteboard and expecting them to survive the rain.
5. You enabled DHCP too early
Pi-hole can serve DHCP, but you do not need that for a basic DNS deployment. Get DNS working first. Add DHCP later if you have a clear reason.
6. You left the default or weak dashboard password
This is still an admin interface. Use a strong password and avoid exposing the dashboard outside your LAN unless you know exactly what you are doing.
FAQ
Do I need macvlan for Pi-hole on Docker?
No. Most beginners do not. Bridge mode is simpler and works well when you map the required ports properly. Macvlan becomes useful in specific network designs, but it is not the starting point.
Can I run Pi-hole on the same host as other Docker services?
Yes, as long as you manage port conflicts carefully. Port 53 is the critical one. The web UI can be moved to 8080 or another unused host port.
Should Pi-hole replace my router's DHCP server?
Not at first. If your router already handles DHCP reliably, keep it there while you learn the DNS side. Once you understand the flow, you can decide whether centralizing DHCP in Pi-hole helps your network.
How do I change the admin password later?
You can update the environment variable in your compose file and recreate the container, or use Pi-hole's management tools from inside the container depending on your version. The official Docker docs and the official container repository are the best references for current syntax.
Is Pi-hole enough for privacy by itself?
It helps a lot, especially against ad and tracker domains, but it is not a complete privacy strategy. DNS filtering is one layer. Good account security, strong passwords, and controlled remote access still matter.
What to learn next
Once Pi-hole is working, the next useful skills are:
- understanding Pi-hole vs AdGuard Home so you know when Pi-hole is the right long-term fit
- tightening remote access with SSH hardening
- organizing client groups with VLANs so DNS policy becomes more intentional instead of all-or-nothing
- self-hosting a second lightweight service like Vaultwarden so your Docker workflow becomes familiar instead of theoretical
The biggest win here is not just ad blocking. It is confidence. Once you understand how DNS, Docker, and client settings fit together, a lot of other homelab services stop feeling mysterious.
And that is the real reason to start with Pi-hole on Docker. It gives you something useful on day one and teaches you patterns you will reuse everywhere else in the lab.
