I care about online privacy and wanted to block ads and trackers across all my devices, not just in browsers. Public DNS providers like Google or Cloudflare are fast, but often log user data. I wanted full control over DNS resolution and to keep it private.
My internet provider (Jio Fiber) uses CGNAT, so I don’t get a public IP address. That makes remote access tricky. This project uses Tailscale to solve that problem while offering secure DNS resolution and ad-blocking.
- Block ads and trackers across all devices
- Encrypt and control DNS traffic (no third-party logging)
- Secure, recursive DNS resolution via Unbound
- Enable remote DNS and Pi-hole admin access even behind CGNAT
- Learn real-world Linux, Docker, VPN, DNS, and firewall management
- macOS (host)
- Colima to run Docker with Lima backend on macOS
- Docker for containerization
- Pi-hole (ad-blocking DNS)
- Unbound (recursive DNS resolver)
- Tailscale (zero-config WireGuard-based VPN)
If you are not using a Mac skip to Section 2.
Install Colima and start it:
brew install colima
colima start --memory 2 --cpu 2 --disk 15Ensure Docker is pointed to Colima:
export DOCKER_HOST=unix://$HOME/.colima/default/docker.sockWent to Tailscale admin panel, go to "Access Controls" and open the Tab "Tags"
Created an ACL tag (e.g. tag:pihole).
Go to the "Settings" and under "Personal Setting" open "Keys" Generated an auth key tagged with tag:pihole Used that auth key in Docker Compose for Tailscale container.
Create the docker-compose.yml with services based on The docker-compose.yml File or download the latest version:
curl -o https://raw.githubusercontent.com/100dollarguy/pihole-unbound-tailscale-dockerized/refs/heads/master/docker-compose.yml- Mac: linux/arm64
- 64bit OS: linux/amd64
- 32bit OS: linux/386 (Only pihole/pihole and tailscale/tailscale offer this Platform. You could go with madnuttah/unbound but will need to adapt the The docker-compose.yml File)
Unbound is listening on port 5335 by default, the compose file maps its to Port 53.
Also the config is mounted to a file on the host machine ("unbound-conf/unbound.conf")
You will need to replace "${AUTHKEY}" with the Key generated in Section 3.
The data files from tailscale are mounted to the folder "tailscale-data" on the host machine.
Pi-hole depends on the other two containers and is using Unbound as its upstream DNS. Set your timezone under the Environment to show the correc date and time in pihole.
Create the necessary folders:
mkdir -p unbound-conf pihole/etc-pihole pihole/etc-dnsmasq.dUnbound requires a root server list and DNSSEC trust anchor.
Download them:
curl -o ./unbound-conf/root.hints https://www.internic.net/domain/named.cachecurl -o ./unbound-conf/root.key https://data.iana.org/root-anchors/root.keyCreate your Unbound config in the "unbound-config" Folder:
touch ./unbound-config/unbound.confEnter the configuration options you want to apply. If you are unsure, go with (the official guide)[https://docs.pi-hole.net/guides/dns/unbound/].
Go Back to the "Access Controls" and click on "JSON Editor". Search for "tagOwners" and add the "acls" sectiona s shown below:
"tagOwners": {
"tag:pihole": ["user:your_email@example.com"]
},
"acls": [
{
"action": "accept",
"src": ["tag:pihole"],
"dst": ["*:*"]
}
]docker compose up -dThe containers should all startup, pihole might take roughly 40s to show up as started and healthy.
The tailscale container auto-joins the network, and Pi-hole is accessible locally.
Ensure Unbound is resolving domains before Pi-hole starts using it:
dig @127.0.0.1 -p 5335 google.comOpen your Tailscale DNS settings. Under "Nameservers" and then "Global Domains" add the Pi-hole container’s Tailscale IP as a global nameserver. Click on the switch named "Override DNS Servers".
Test DNS queries from remote Tailscale connected devices:
nslookup google.com 100.x.x.xExpected output:
Name: google.com
Address: 142.251.221.110
🤯 What I Learned
- Difference between recursive and forwarded DNS
- Tailscale ACLs and DNS overrides
- Docker networking and port mappings
- Unbound configuration for Pi-hole
- Colima/Lima integration for macOS-based Docker usage
🚀 Future Plans
- Add DNS-over-HTTPS fallback
- Setup Headscale server
- Use Raspberry Pi or always-on NUC
- Integrate 2FA for Pi-hole admin
- Add logging and monitoring for DNS performance
This project is licensed under the MIT License. You're free to use, modify, and share it — personally or commercially.
Feel free to fork it, improve it for your own setup, or share with others!




