Head to head
Pi-hole vs AdGuard Home
Both block ads for every device on the network. They differ on encrypted upstreams, how policy is scoped per device, and how fast you can undo a block when someone complains.
Should you install Pi-hole or AdGuard Home?
Install AdGuard Home. It speaks DNS-over-HTTPS, DNS-over-TLS, DNS-over-QUIC and DNSCrypt as both client and server with nothing bolted on, per-client policy is a first-class concept, and it idles around 60 MB. Choose Pi-hole instead if you specifically want its group model for slicing policy across many devices, or if you depend on the larger community list and tooling ecosystem built around it. Both block the same ads; the difference is what happens when you need encryption, per-device exceptions, or a fast fix at 8pm.
The blocking is a tie. Both resolve queries, both check the name against subscribed lists, both answer with nothing when it matches, and both use largely the same lists. If ad blocking were the whole job you could flip a coin.
Three things are not a tie: whether encrypted DNS is in the binary or a second daemon, how policy is scoped to a device, and what the last two years of releases did to people who were already running it. Those decide it.
For a new install in 2026, install AdGuard Home. Keep reading for the cases where Pi-hole is the better answer, because there are two real ones.
The rows that separate them#
Ignore the license row and the ops load row, which are close enough to be noise. Look at the port list: AdGuard Home ships with 853 and 3000 in its defaults because it serves DNS-over-TLS itself and runs a first-boot wizard. Pi-hole's port list is 53, 80 and 443 because since v6 it wants the web ports for its own server.
| Specification | Pi-hole | AdGuard Home |
|---|---|---|
| Licence | EUPL-1.2 (Copyleft) | GPL-3.0 (Copyleft) |
| Written in | C (pihole-FTL) and Bash (core), JavaScript web UI | Go, with a React web interface |
| First release | 2015 | 2018 |
| Maturity | Mature | Mature |
| Datastore | SQLite (gravity.db and pihole-FTL.db) plus a TOML config file | A single YAML config file plus BoltDB files for stats and sessions; the query log is a JSON lines file |
| Services to run | 1 | 1 |
| Idle memory | 100 MB | 60 MB |
| Memory in use | 220 MB | 150 MB |
| Operational load | 2 / 5, Light | 2 / 5, Light |
| Identity | Local accounts only | Local accounts only |
| arm64 builds | Yes | Yes |
| Default ports | 53, 80, 443 | 53, 80, 443, 853, 3000 |
| Backup shape | SQLite backup | Embedded key-value store |
Pi-hole's documented floor is 512 MB RAM and 2 GB of free disk, 4 GB recommended. AdGuard Home publishes no minimum, and the driver for its memory is how many filtering rules you load, not how many queries you serve. Both fit on a Raspberry Pi 3 without drama.
Encrypted upstreams: built in against bolted on#
Your resolver forwards queries somewhere. Over plain UDP 53, your ISP sees every domain you look up, blocking or not.
AdGuard Home handles this in configuration:
dns:
upstream_dns:
- https://dns.quad9.net/dns-query
- tls://one.one.one.one
bootstrap_dns:
- 9.9.9.10Pi-hole handles it by running another program. The standard arrangement is a cloudflared container on the same host, listening on 127.0.0.1:5053, with Pi-hole configured to forward there. It works. It is also a second container to upgrade, a second process that can fail, and when it does fail Pi-hole reports upstream errors rather than falling back to something safe.
The direction that matters more is inbound. AdGuard Home can be a DoH and DoT server, so a phone on mobile data can dial your home resolver over 853 with a real certificate and get your filtering with no VPN. Pi-hole cannot do that at all; you put it behind Headscale or wg-easy and route DNS over the tunnel, which is a fine answer but a different one.
Per-client policy: two different models#
Pi-hole thinks in groups. You define a group, assign clients to it, then attach adlists, allow entries and deny entries to the group. It is a proper many-to-many model and it is the better one when you have twelve devices falling into three policies: kids strict, adults normal, work laptop unfiltered.
AdGuard Home thinks in clients. A client, identified by IP, MAC, a ClientID string or the path in its personal DoH URL, carries its own upstreams, its own blocklists, its own safe-search and parental settings. Custom filtering rules also take a $client modifier, so a single line can carve out one device:
@@||analytics.example.com^$client=192.168.1.50That is the difference in practice. Pi-hole is better at "these six devices share a policy". AdGuard Home is better at "this one device needs an exception right now", which is the situation you are actually in most evenings.
Which one survives someone saying "the site is broken"#
This is the real operational test, and it is worth being concrete about it.
Someone in the house cannot check out on a shopping site. You have maybe two minutes before they give up and blame the server. The sequence is: find the blocked domain, allow it, confirm.
Both products have a query log you can filter by client and a button that turns a blocked query into an allow rule. Where they diverge is the blast radius of that click. In Pi-hole, an allow entry is global unless it belongs to a group, so a fast fix loosens filtering for everyone, and doing it properly means creating or picking a group and confirming the client is in it. In AdGuard Home you can scope the same fix to the one client with a $client modifier, or just turn protection off for that client for ten minutes from its settings.
The other half of the test is when the resolver itself is the problem. Pi-hole's advantage here is real: it is the reference platform for a decade of tutorials, and the answer to almost any "why is Pi-hole doing this" question already exists somewhere. AdGuard Home's documentation is thinner and its community is smaller.
Verdict on this specific test: AdGuard Home, narrowly, because scoping the fix to one device is one line rather than a group restructure. If your household needs the same fix applied to eight devices at once, Pi-hole's groups win instead.
What the Pi-hole v6 rewrite broke#
Pi-hole v6 landed in February 2025 and rewrote the parts you touch. As of September 2026 the line is FTL v6.7, Web v6.6, Core v6.4.3, and people are still tripping over the transition.
- lighttpd and PHP are gone.
pihole-FTLserves the web UI and a documented REST API itself. It binds 80 and 443 and only falls back to 8080 if something already owns 80, which is a fight on any host that also runs a reverse proxy. An in-place upgrade offers to disable lighttpd, and saying yes takes down anything else lighttpd was serving. setupVars.confis no longer read. Everything moved to/etc/pihole/pihole.toml. Older tutorials and Ansible roles still write to the old file, changing nothing at all.- The static API token is gone. v5 integrations that sent
?auth=<token>all broke. You POST your password to/api/auth, get a session ID, and pass it asX-FTL-SID. For scripts, generate an application password in the UI, which is also the only way to authenticate once TOTP is on. - Every Docker environment variable was renamed.
WEBPASSWORDtoFTLCONF_webserver_api_password,DNS1/DNS2toFTLCONF_dns_upstreams,WEB_PORTtoFTLCONF_webserver_port,DNSSECtoFTLCONF_dns_dnssec, andServerIPremoved with no replacement. The container starts happily with the old names, ignores them, and you get a Pi-hole with a random password and default upstreams. No error anywhere.
Also worth knowing before you write Ansible for either: any setting supplied to Pi-hole as an FTLCONF_ variable becomes read-only in the web interface, and AdGuard Home serializes its in-memory state back over AdGuardHome.yaml whenever settings change, so edits made while it is running are lost. Neither is friendly to declarative configuration, in opposite ways.
The uncomfortable truth about both#
Neither of these does what its dashboard implies. They see only the queries that reach them on port 53. Firefox's DNS-over-HTTPS, Chrome's secure DNS, an iPhone with a private DNS profile, a smart TV with 8.8.8.8 compiled in: all invisible, all unfiltered. The pretty percentage on the dashboard is a percentage of what you can see.
Fixing that is a firewall job, not a DNS job. Block outbound 853, redirect or block outbound 53 to anything except your resolver, and block the known DoH endpoint domains. Do that and the numbers start meaning something.
Second uncomfortable truth: whichever you pick becomes a single point of failure for name resolution on your entire network. When it is down, nothing resolves, and the complaint is not "ads are back", it is "the internet is broken". Budget for a second instance or a fallback before you hand this out by DHCP. Uptime Kuma watching a DNS query against it is the cheapest early warning you can set up.
Which one for your situation#
| Situation | Use | Why |
|---|---|---|
| New install, no existing setup | AdGuard Home | Encrypted upstreams and per-client policy with nothing bolted on |
| You want filtering on a phone off the LAN | AdGuard Home | It serves DoT on 853 and DoH itself |
| Twelve devices in three policy groups | Pi-hole | The group model is genuinely better for shared policy |
| You want DHCP served by the same box with hostnames in the log | Either | Both do it; Pi-hole's log presentation is better |
| Config managed by Ansible or in git | Neither, comfortably | AdGuard rewrites its own YAML; Pi-hole makes env-supplied settings read-only |
| Running on a 32-bit router or old NAS | AdGuard Home | Builds for 386, armv6, armv7, arm64, amd64 and ppc64le |
| You need authoritative DNS or DNSSEC signing for your own zone | Neither | Technitium DNS Server is the right tool |
| Already running Pi-hole v6 happily | Pi-hole | There is no payoff here worth a migration |
What to do next#
Pick one, then do the part everyone skips: decide what answers DNS when that box is rebooting. DNS for self-hosters covers split horizon, internal names and the secondary-resolver question, which matters more to your household than which of these two you chose. Back up AdGuardHome.yaml or /etc/pihole on a schedule, since both hold your entire configuration in a file small enough to commit to a private git repo, and see Backups that actually restore for making that automatic. The rest of the category is at Networking.
Questions#
Does Pi-hole support DNS-over-HTTPS?
Only with a second program alongside it. Pi-hole forwards to whatever upstream you configure, so DoH means running cloudflared or a similar proxy on the same host and pointing Pi-hole at 127.0.0.1. That works and thousands of people run it, but it is another container to upgrade, another process that can die, and Pi-hole will happily keep serving plain DNS if it does. AdGuard Home has DoH, DoT, DoQ and DNSCrypt in the binary, as an upstream client and as a server.
Can either one filter differently for the kids' tablet?
Both can, with different mechanics. Pi-hole uses groups: you create a group, assign clients to it, and attach adlists and allow or deny entries to that group. AdGuard Home attaches settings directly to a client identified by IP, MAC, ClientID or its own DoH URL path, including different upstreams, different blocklists and safe search, and its custom rules accept a $client modifier so one allow rule can apply to a single device. Pi-hole's model is better when many devices share one policy; AdGuard's is better when policy is genuinely per device.
What broke in the Pi-hole v6 upgrade?
Almost everything you touch. lighttpd and PHP were removed and pihole-FTL now serves the web UI and REST API itself on 80 and 443. setupVars.conf is no longer read; configuration lives in /etc/pihole/pihole.toml. The v5 static API token is gone in favor of a session from /api/auth or an application password. In Docker, WEBPASSWORD became FTLCONF_webserver_api_password and DNS1/DNS2 became FTLCONF_dns_upstreams, and the container ignores the old names silently rather than erroring.
Will a DNS blocker stop ads in apps and on smart TVs?
Sometimes, and less than you expect. It only sees queries that arrive on port 53. Firefox's DNS-over-HTTPS, Chrome's secure DNS, iOS and Android private DNS profiles and televisions with hardcoded resolvers all walk straight past it, and the dashboard shows a suspiciously quiet device. The mitigations are firewall rules: block outbound 853, block outbound 53 to anything but your resolver, and block known DoH endpoint domains. Neither product can fix this from the inside.
Do I need to run two of them?
If you hand out a single DNS server by DHCP, that box becomes the thing that takes the whole network down when you reboot it. Two instances is the usual answer, and neither project offers built-in sync: for AdGuard Home people copy AdGuardHome.yaml, for Pi-hole they use the Teleporter export or a third-party sync tool. A cheaper alternative is handing out your router as a secondary, accepting that some queries go unfiltered when the primary is down.
Is AdGuard Home really still on version 0.107?
Yes. Eight years in, the current release is 0.107.79 (August 2026), and the project does not do semantic versioning. It is dependable software with a misleading version string, but the config file carries a schema_version that bumps and rewrites your YAML on upgrade, and downgrading after that fails because older builds do not understand the newer schema. Copy AdGuardHome.yaml before every upgrade; that copy is your only rollback.
Sources#
- Pi-hole v6 announcement, architecture changes
- Pi-hole documentation, Docker v5 to v6 environment variables
- Pi-hole documentation, REST API authentication
- Pi-hole documentation, hardware prerequisites
- AdGuard Home repository README and feature comparison
- AdGuard Home changelog, versions and schema migrations
- AdGuard Home wiki, Docker ports and volumes
- AdGuard Home Docker image, published architectures
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.