Guide · OpenWrt
AdGuard Home on OpenWrt: install it without breaking DNS
AdGuard Home on OpenWrt gives you network-wide ad blocking, tracker filtering and parental controls on the router itself, with no subscription and no cloud account. Every device on the LAN gets filtered DNS whether it wants to or not, including the ones you can't install an app on: televisions, consoles, the thermostat.
The install is a single package. The hard part is not the install. It is port 53, because OpenWrt already has a DNS daemon running there, and the two will fight. This guide covers the package as it exists in the official feed today, what it puts on disk, how to hand DNS over cleanly, and how to set up parental controls that hold up.
Versions, and why the package matters
AdGuard Home's latest stable release is v0.107.80, published on 3 November 2026. It is a dependency and security release: Go moved to 1.27.3, Multipath TCP support was disabled to mitigate CVE-2026-98432, and macOS 13 Ventura support was dropped. (release notes)
The OpenWrt packages feed builds adguardhome 0.107.80-1 — the same version as upstream, so there is no lag to work around right now. (packages feed Makefile) Current OpenWrt stable is 25.12.6, with 24.10.8 as oldstable; the commands below are the same on both.
What the package actually installs
The package is GPL-3.0-only, maintained in the packages feed by Dobroslaw Kijowski and George Sapkin, and depends on ca-bundle — needed for DNS-over-HTTPS and DNS-over-TLS upstreams and for downloading filter lists over TLS. (Makefile)
It creates a dedicated service user and group, adguardhome, with uid and gid 853. That user is how AdGuard Home binds port 53 without running as root: the init script runs it under procd with no_new_privs 1 and capabilities read from /etc/capabilities/adguardhome.json. (init script)
Configuration is split in two, and both halves are marked as conffiles — meaning the package manager will ask before overwriting them:
/etc/config/adguardhome— the UCI service settings./etc/adguardhome/adguardhome.yaml— AdGuard Home's own YAML, where the DNS listen settings, upstreams and web interface address live.
The YAML must sit in its own directory. If you put it back at /etc/adguardhome.yaml, the init script refuses to start with "AdGuard Home config must be stored in its own directory, and not in /etc". A uci-defaults migration moves a legacy file into the new location for you.
Runtime data — query log, statistics, sessions — lives in /var/lib/adguardhome, the work_dir UCI option. The init script creates it with mode 0700 and chowns it to the service user on every start. Backups should therefore cover both /etc/adguardhome/ and that work directory, not just the YAML.
The port 53 problem, stated plainly
AdGuard Home is not the only DNS daemon on an OpenWrt box. dnsmasq ships with the firmware and also wants port 53. The AdGuard Home init script starts at START=19, with the comment "# matches dnsmasq" — it is deliberately launched in the same slot, which is exactly why the two compete.
So decide ownership before you enable anything. Three shapes work:
- AdGuard Home takes DNS, dnsmasq keeps DHCP. AdGuard Home has its own DHCP settings page, so this is a configuration choice, not a limitation. Turn off dnsmasq's DNS role and leave its DHCP role alone.
- AdGuard Home takes both. Move DHCP from dnsmasq to AdGuard Home's DHCP page, then retire dnsmasq.
- Chain them. Leave dnsmasq answering clients and set AdGuard Home as its upstream — but then AdGuard Home's own DHCP and per-client features are doing less work than they could.
Whichever you pick, one daemon has to be off port 53 or the second one to start will fail. AdGuard Home's listening ports are not UCI options, by the way — http.address and the DNS settings live in the YAML, so port changes go through the web interface or by editing adguardhome.yaml.
Installing it
Install the package from the command line on the router, then start it once so it writes its initial files:
opkg update
opkg install adguardhome
/etc/init.d/adguardhome enable
/etc/init.d/adguardhome start
On OpenWrt 25.12 the package manager is apk rather than opkg; the package name is unchanged, so swap the command and keep going. The service is a procd service with respawn enabled, so it comes back on its own if it dies — which also means a broken config will loop rather than fail once.
First start: the wizard on port 3000
On first run AdGuard Home listens on 0.0.0.0:3000 and prints the URLs to open for the setup wizard. Browse to the router's address on port 3000, choose an admin username and password, and set the DNS listen interface and web interface port. (AdGuard Home getting started)
The ports involved are 3000/TCP for setup, 80/TCP for the web interface afterwards, and 53/UDP plus 53/TCP for DNS. If you keep the dashboard on 80, it collides with LuCI; many people move it, and the setting is http.address in the YAML — for example 0.0.0.0:8080, 0.0.0.0:0 for all interfaces on an ephemeral port, or 127.0.0.1:0 for loopback only. (AdGuard Home FAQ)
Parental controls
This is where most people actually end up with AdGuard Home, and it is not buried. Parental Control is switched on from Settings → General settings, the same page that carries "Block domains using filters and hosts files". The lists behind it are managed under Filters: DNS blocklists, DNS allowlists, DNS rewrites and custom filtering rules.
Two details worth knowing before you promise anyone a filtered network:
- AdGuard Home inspects responses, not just requests. It blocks CNAME cloaking — the trick where an allowed hostname resolves through a chain to a tracker domain — and the query log shows it as "Blocked by CNAME or IP". If a rule names a tracker domain but the request looks like a legitimate host, that is the line to look for.
- Custom block pages are a YAML edit. Pointing Parental Control or Safe Browsing at your own block page uses
dns.parental_block_hostanddns.safebrowsing_block_hostset to the IP of a local HTTP server. Neither is exposed in the web interface.
The service-level settings in /etc/config/adguardhome — verbose, gc, maxprocs, memlimit, the jail mounts — are all per-service, not per-client. Filtering policy itself is set inside AdGuard Home, which is one more reason to keep the two config files straight in your head and in your backups.
Running it on a small router
On a 128 MB router, memory is the real limit, and the package knows it: gc maps to GOGC, memlimit to GOMEMLIMIT, and maxprocs to GOMAXPROCS. Tightening those in /etc/config/adguardhome is the supported lever. Query history also grows in /var/lib/adguardhome, so shorten retention rather than letting statistics fill the overlay.
If the router is also the Wi-Fi access point and the DHCP server, watch what you ask of it. Splitting IoT devices onto their own network — see VLANs on OpenWrt — reduces the noise AdGuard Home has to log, and gives you a second place to apply rules.
Updating, and why the button is greyed out
The init script runs AdGuard Home with --no-check-update. Self-update from the web interface is disabled on purpose: upgrades come from the package manager so that the binary, the conffiles and the init script stay in step. Update the package, restart the service, and keep an eye on syslog for the config migration messages.
Logs and troubleshooting
The service logs to syslog with --logfile syslog, so read it with logread -e AdGuardHome. That is where a failed port bind or a bad YAML shows up.
If it is running but "not blocking", AdGuard's FAQ gives an ordered checklist: confirm with nslookup that the server answering is actually the AdGuard Home IP; confirm the interface is being listened on; confirm blocking with filters and hosts files is on; look for an allowlist exception; look for a DNS rewrite or custom rule overriding the block.
No LuCI app, and that is fine
Neither the OpenWrt project nor AdGuard ships an official LuCI app. AdGuard's documentation lists an OpenWrt LuCI app maintained by @kongfl888 under *other/unofficial* options, and the same page lists the Home Assistant add-on maintained by @frenck as an unofficial route for people who already run Home Assistant. The supported path is UCI for the service and AdGuard Home's own web interface for everything else.
Where this fits: AdGuard Home is the DNS half of a filtered home network, not the whole thing. For the bigger picture, start with OpenWrt, explained, then look at segmentation and OpenWrt vs DD-WRT if you are still picking a platform.
FAQ
Does AdGuard Home replace dnsmasq on OpenWrt, or do they conflict?
They conflict by default, because both want port 53. OpenWrt ships dnsmasq as its DNS forwarder and DHCP server, and the AdGuard Home init script starts at START=19 — the same slot as dnsmasq, with the comment "matches dnsmasq". Before you enable the service, decide which daemon owns port 53 and stop or repoint the other one.
How do I stop dnsmasq from fighting AdGuard Home for port 53?
Decide the split first: one daemon answers DNS on port 53, the other does not. AdGuard Home has its own DHCP settings page, so "dnsmasq keeps DHCP and AdGuard Home takes DNS" is a configuration choice rather than a limitation of the package. If AdGuard Home cannot bind port 53 it will fail at start, and the error appears in syslog.
How do I set up AdGuard Home parental controls on OpenWrt?
Open the AdGuard Home web interface and go to Settings, then General settings, where Parental Control sits on the same page as "Block domains using filters and hosts files". The blocklists behind it live under Filters, in DNS blocklists. A custom block page needs dns.parental_block_host in adguardhome.yaml; it is not exposed in the UI.
Where are AdGuard Home logs on OpenWrt, and how do I turn on verbose logging?
The OpenWrt package runs AdGuard Home with --logfile syslog, so logs go to the system log and are read with logread -e AdGuardHome. The verbose option in /etc/config/adguardhome turns up the detail; query logs are stored separately in the work directory rather than in syslog.
Can I run AdGuard Home on a router with 128 MB of RAM?
It will start, but memory is the constraint — which is why the init script exposes gc (GOGC), memlimit (GOMEMLIMIT) and maxprocs (GOMAXPROCS) as UCI options. Query history and statistics also live in the work directory, so trim retention on a small box instead of assuming the defaults fit.
How do I update AdGuard Home on OpenWrt — the UI button or the package manager?
The package manager. The init script runs AdGuard Home with --no-check-update, so the self-update button is disabled by design. Upgrades arrive as a new adguardhome package version; the feed currently builds 0.107.80-1, the same version as upstream's 0.107.80 release.
Why is AdGuard Home not blocking ads after install?
AdGuard's own FAQ checklist, in order: the client is not actually pointed at AdGuard Home (verify with nslookup which server answers), the interface is not being listened on, blocking with filters and hosts files is switched off, an allowlist entry is overriding the block, or a DNS rewrite or custom rule is overriding it.
Do I need a LuCI app to manage AdGuard Home on OpenWrt?
No. There is no official LuCI app from either project. AdGuard's documentation lists an OpenWrt LuCI app maintained by @kongfl888 under unofficial options, and the package is configured through UCI plus AdGuard Home's own web interface on port 80 after setup.
Related: OpenWrt for the smart home and IoT VLANs on OpenWrt.