Pi-hole on Synology Docker
The ads on my kids' tablet were what finally pushed me to try Pi-hole. It's a DNS sinkhole: every device on the LAN asks it for names, and anything on a blocklist of ad and tracking domains resolves to nothing. People run it on a Raspberry Pi, but my Synology is on all the time anyway and sits next to the router, so I put it there as a container in bridge mode.
You need the Docker package on an x86-64 Synology (DSM 6.1), SSH access, and a fixed IP on the NAS (Control Panel → Network → Network Interface → Edit → Use manual configuration). Nothing else can be on port 53, so the Synology DNS Server package must be stopped or uninstalled. The DSM-specific port conflicts are the part that will trip you up.
Data folders
Pi-hole keeps its configuration in two directories that must persist across container updates:
sudo mkdir -p /volume1/docker/pihole/etc-pihole
sudo mkdir -p /volume1/docker/pihole/etc-dnsmasq.d
The first holds the gravity database, whitelist and blacklist and the web password hash. The second holds any custom dnsmasq configuration, such as local hostnames or conditional forwarding for your router's domain.
Compose file
The image most people use is diginc/pi-hole, maintained by a Pi-hole developer. Create /volume1/docker/pihole/docker-compose.yml:
version: '2'
services:
pihole:
image: diginc/pi-hole:alpine
container_name: pihole
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "8053:80/tcp"
environment:
ServerIP: 192.168.1.10
TZ: Europe/Berlin
WEBPASSWORD: choose-a-password
DNS1: 1.1.1.1
DNS2: 9.9.9.9
volumes:
- /volume1/docker/pihole/etc-pihole:/etc/pihole
- /volume1/docker/pihole/etc-dnsmasq.d:/etc/dnsmasq.d
dns:
- 127.0.0.1
- 1.1.1.1
Points to note:
ServerIPmust be the LAN address of the NAS. Pi-hole uses it to answer for blocked domains and for its own web interface; getting it wrong produces a working blocker with a dead admin page.- The web interface is published on 8053 instead of 80 because DSM's nginx owns 80 and 443.
- The
dnsentries make the container resolve through itself first, with a fallback so the initial blocklist download works before Pi-hole is up.
Start it and read the logs
cd /volume1/docker/pihole
sudo docker-compose up -d
sudo docker logs -f pihole
The first start downloads and compiles the default blocklists, which shows as [✓] Pi-hole blocking is Enabled style lines followed by a domain count of around 100,000 depending on the lists shipped with the image. When you see dnsmasq: started, version ... and the lighttpd startup line, it is ready.
Test one client before touching DHCP
Don't repoint the whole network until a manual query works. I once broke DNS for the entire house that way on a Sunday evening. From a Linux or Mac workstation:
dig @192.168.1.10 www.synology.com +short
dig @192.168.1.10 doubleclick.net +short
The first should return real addresses. The second should return 192.168.1.10, the Pi-hole itself, which is how a blocked domain looks. On Windows, nslookup doubleclick.net 192.168.1.10 shows the same.
Open http://192.168.1.10:8053/admin/ and log in with the password from the compose file.
Point the network at it
The clean way is to change the DNS server that the router hands out over DHCP to 192.168.1.10, leaving the router itself as gateway. Every device picks it up at its next lease renewal; forcing a reconnect speeds this up.
The NAS itself should not use Pi-hole as its own resolver, otherwise DSM cannot resolve anything while the container is down for an update. Keep Control Panel → Network → General → DNS Server pointing at the router or a public resolver.
After a day or so
After a day, the dashboard shows the query count and the percentage blocked; 10 to 25 percent is typical for a household with phones and a smart TV. In the Query Log, check that client names or addresses look right. If every query appears to come from the Docker bridge address 172.17.0.1, the container is not seeing real client IPs; that is cosmetic in bridge mode and expected, but it does mean the per-client statistics are useless. Switching the container to host networking fixes it at the cost of needing port 80 free, which on DSM you cannot have; macvlan networking is the real solution and deserves its own post.
Errors I've hit
docker: Error response from daemon: driver failed programming external connectivity on endpoint pihole: Error starting userland proxy: listen udp 0.0.0.0:53: bind: address already in use. Something on the NAS already owns 53. Check withsudo netstat -ulnp | grep :53; the usual culprit is the DNS Server package or a previous Pi-hole container that was not removed.- Blocklists fail to update with
curl: (6) Could not resolve host. The container has no working upstream during gravity update. Make sure thednslist in the compose file includes a public resolver, not only 127.0.0.1. - Pages load slowly or time out while blocking works. Some clients try the blocked IP on port 443 and wait for a timeout. Pi-hole answers HTTP on the
ServerIP, but not HTTPS; this is inherent to the approach and the only real fix is a shorter client timeout or using the NULL blocking mode offered in newer FTL builds. - The admin password is rejected after recreating the container.
WEBPASSWORDis only applied when/etc/pihole/setupVars.confdoes not already hold a hash. Reset it withsudo docker exec pihole pihole -a -p newpassword.
After that it's a docker-compose pull && docker-compose up -d every month or two. Whitelist the odd false positive from the admin page; it survives updates because /etc/pihole lives on the NAS volume.