[How-To-Updated 2021] Installing AdGuardHome on OpenWrt [Manual and opkg method]

Yes. It is a much safer option and its why I have a separate WAN DNS script so you can setup the router to bypass your ISP DNS for a more reliable provider like Cloudflare... (kinda ironic considering their major outage recently). You can pick any DNS provider you wish. I just like Cloudflare because usually is the fastest DNS service.

But looping the router DNS back through AGH is NOT recommended NOR is it required.
You can even set up your router with a different DNS to the DOH DNS that AGH uses for some redundancy.

The combination of NTP issues and also AGH's position in the startup queue does cause issues. It really requires some diagnostics and a patch issuing to fix that. But the AGH team I'd prefer them to fix the DHCP issues so I can make this whole process simpler by doing a complete cutover from OpenWrt DNS/DHCP straight to AGH DNS/DHCP which would avoid some of the more complex surgery that I have developed for this process.


Especially when there are so many to keep track of.

1 Like

BTW, sorry for my limited knowledge. Right now I am using dnsmasq`s DHCP (not AGH DHCP).

How do I make the router itself (ssh session to OpenWrt for example) resolve both (1) general DNS and (2) local DHCP hostnames without having OpenWrt land DNS to back through AGH?

I mean, if I have OpenWrt configured to use an upstream DNS (such as, it will bypass AGH (and will resolve internet hostnames) but it will not be able to resolve local hostnames assigned via DHCP/dnsmasq.

However, if I configure OpenWrt to back through AGH (network.lan.dns='') which is not recommended, it will resolve both local hostnames (since AGH is configured to use for local PTR queries - and yes, I am still using port 5353).

1 Like

use the /etc/hosts file.

You might not have seen that because i edited it in after i realised you were doing this directly from your router.

The other way is to do what is called domain interception.

https://openwrt.org/docs/guide-user/base-system/dhcp_configuration#selective_dns_forwarding using this to tell dnsmasq to forward those requests to your AGH instance will work and cover all internal domains requests.

Internally we do this with AGH so downstream clients get searched for first.
What you are wanting to do is have your router be aware DNS wise of its downstream clients. Thus you want selective forwarding of those requests to AGH but everything else go upstream to your external DNS.

1 Like

Thanks. While /etc/hosts could work for static/reserved IPs, it does not work with dynamically assigned addresses (which is a nice feature of dnsmasq being able to resolve DHCP hostnames).

But I'm not sure I understand your suggestion of domain interception (probably due to my limited knowledge! :blush:).

Please notice that I am not talking about OpenWtr's dnsmasq server. I am talking about configuring a regular OpenWrt interface to act as a client of AGH. I really fail to understand why this is would not be desirable (expect the ntp issue when using DoH/DoT which has a simple workaround as already discussed):

  1. Everything configured per your recommendations in AGH wiki with local domain interception ([/lan/] [//] and Private Reverse DNS Server (
  2. All clients in the network will use (AGH) as configured via DHCP
  3. Then I changed only the local lan interface DNS client to point to AGH (uci set network.lan.dns=''). No dnsmasq configuration change involved as far as I understand (however TBH I'm not sure how this configuration affects OpenWrt's dnsmasq)
  4. In the router itself, for Internet DNS queries: OpenWrt (Client) -> query to (AGH) -> upstream DNS server.
  5. In the router itself, for local DNS queries: OpenWrt (Client) -> query to (AGH) -> (local dnsmasq DNS server via local domain interception).

In step 5, since the local OpenWrt dnsmasq DNS server is aware both of static and dynamic DHCP reservations, it will correctly resolve the local host names. If I omit step 3, the query in step 5 will fail.

So, why would you advice against using the configuration in step 3 above?


Its not ONE service. Its TWO separate services and issues.

  • Your router DNS has an upstream external DNS. Normally it would also see downstream but we moved dnsmasq and thus it is no longer aware of it.

  • AGH ALSO has an upstream DOH provider and downstream clients. Using DI and rDNS AGH is aware and sees internal clients.

Firstly the loop.
Its bit complex but, essentially the problem boils down to this. By looping the router queries into AGH, you create a loop of DNS. Combined with that is AGH takes a while to load (it loads after dnsmasq does and also after NTP try's to update.) Thus doing this creates a broken loop that breaks things like NTP etc. The NTP bypass I explain will work if AGH is up, but due to AGH being loaded last in the boot sequence we are trying to talk to a service that has not started. And thus the breakage.

To prevent this we set the router DNS to point upstream separately to do unencrypted and unfiltered DNS to completely avoid AGH start up times. The router boots, gets DNS and NTP updates and is happy, then AGH service loads and becomes the upstream for your downstream clients (your LAN devices).

What you require is your Router to resolve your downstream client ips via hostnames.

This can be simply done by using etc/hosts for fixed address but you want a dynamic method.

That we do via Domain Interception. This tells the router that local lookups or domainless lookups are to be passed to dnsmasq to resolve, instead of upstream to your external DNS (Which reply's NXDOMAIN as it doesn't know they exist)

Normally we only require DI for downstream clients and thus my instructions cover this. But you want to do DI for the router as well. So its an additional step so your router is aware the same lookup route as your downstream clients use. BUT and this is the important bit. You do NOT want to route your router lookup into AGH. This is because AGH doesn't hold the info. The local client info you require is in the redirected Dnsmasq. What we are doing here is a repeat of setting up AGH by telling the DNS service to refer (downstream) to the internal lookup provided by Dnsmasq, rather than the upstream external DNS.

So what you need for the router is DI is to tell it to search dnsmasq then go to the upstream. Same as AGH does.

(edit) OMFG... ok now I feel an idiot. In paring down dnsmasq so it only served local PTR requests I disabled the ability of the router to do its own internal lookups. I thought the setting just stopped it using /etc/resolve.conf but it disables all PTR for the router.

uci set dhcp.@dnsmasq[0].noresolv="1"

This needs to be set to "0" and then it will work. I will fix the wiki and thread documentation for all this.

Have to admit thou. 6 months and no one pointed it out. :frowning: :boom:

(edit2) The quick fix to sort this is do the following:

uci set dhcp.@dnsmasq[0].noresolv="0"
uci commit dhcp
/etc/init.d/dnsmasq restart

(edit3) Further research shows what I should have done is both disable dnsmasq but also enforce local lookups.

uci set dhcp.@dnsmasq[0].noresolv="1"

uci set dhcp.@dnsmasq[0].localuse="1"


Now we are talking!!!! :smiley:

uci set dhcp.@dnsmasq[0].localuse="1" is indeed the solution I was looking for! Thank you!!!

1 Like

A different problem now. With all settings corrected (NTP now back to defaults), AGH started OK after a reboot. While it resolves all DNS queries OK, filtering stops working after a reboot (ads were not being blocked).

The reason is that the blocklists are not being updated after reboot (rules count = 0):

After manually pressing "Check for updates" the rules count is updated and ad blocking starts working again:

Could this is also related to DoH/DoT being used and the rules not being updated due to the fact they are using HTTPS before NTP updates system time?

EDITED This issue does not seem to be related to DoH/DoT. I've just changed AGH upstream to plain (instead of DoT) and the issue still happens (lists not updated after a reboot). I will not have more time to investigate this today nor tomorrow. On Friday I will follow-up on this issue.

If you are using the manual thread version then they should be in your /opt/AdGuardHome/data/filters folder and thus loaded from there on boot.

If you are using the opkg version then your filters are not stored. They are lost on reboot as they are stored in ram. They should reload on reboot however. Check your logs to see what's going on.

I suspect you are using the opkg version and it is trying to do updates before the network is up fully and thus fails to update. James was looking at changing the opkg version to a later startup time.

The fix for this is to edit the /etc/init.d/AdGuardHome file and change the start variable.

#!/bin/sh /etc/rc.common



Changing it to 95 will delay AGH start long past the network startup and things will work fine.

I believe the opkg version uses 25 and this is too early in the boot process. It doesn't always happen but it depends on your router and how quickly it loads the network interface. It is a bug but it is being discussed. The correct timing is up for question however.


Thank you, and yes, I'm using the opkg version.

Unfortunately changing /etc/init.d/adguardhome as you recommended did not solve the problem. I waited up to 5 minutes after a reboot and the blocklists were not automatically updated.

Just please confirm the case of the filename: you suggested changing /etc/init.d/AdGuardHome (capitalized), but the existing filename is all lowercase (which is the one I used that I believe it is correct).

I will keep investigating, any other advice is very welcome. Thank you again!!!

EDIT 1: I believe it might be a WAN timing issue after reboot (see below), still investigating. Manually executing service adguardhome restart after a reboot does update the lists.

EDIT 2: finishing for today. Since OpenWrt's crontab does not support @reboot, for now a quick and dirty solution was to add the following command to /etc/rc.local to restart AGH 30 seconds after reboot so it can update the blocking lists.

# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

sleep 30 && service adguardhome restart &
exit 0
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.901231 [info] requesting filter from https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt, skip: Get "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt": dial udp connect: network is unreachable
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.901295 [error] os.Chtimes(): chtimes /tmp/adguardhome/data/filters/1.txt: no such file or directory
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.901314 [info] Failed to update filter https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt: Get "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt": dial udp connect: network is unreachable
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]:
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.901566 [info] requesting filter from https://adaway.org/hosts.txt, skip: Get "https://adaway.org/hosts.txt": dial udp connect: network is unreachable
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.901622 [error] os.Chtimes(): chtimes /tmp/adguardhome/data/filters/2.txt: no such file or directory
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.901641 [info] Failed to update filter https://adaway.org/hosts.txt: Get "https://adaway.org/hosts.txt": dial udp connect: network is unreachable
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]:
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.902183 [info] requesting filter from https://abp.oisd.nl/basic/, skip: Get "https://abp.oisd.nl/basic/": dial udp connect: network is unreachable
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.902310 [error] os.Chtimes(): chtimes /tmp/adguardhome/data/filters/1656519832.txt: no such file or directory
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:56:32.902353 [info] Failed to update filter https://abp.oisd.nl/basic/: Get "https://abp.oisd.nl/basic/": dial udp connect: network is unreachable
Thu Jun 30 08:56:32 2022 daemon.err AdGuardHome[959]:
Thu Jun 30 08:57:47 2022 daemon.err AdGuardHome[959]: 2022/06/30 11:57:47.670644 [info] auth: user "root" successfully logged in from ip
1 Like

Yes the opkg is all lowercase. The full install has capitals. But changing the start value should have delayed AGH till after your network is up. I personally dont use the opkg version. It was developed as a package separately and only follows their stable branch. However the edge version does get fixes faster.

You could try pushing the Start value to 99. See if that gives you the extra delay you need. But a sleep and restart is doing what you require.

I'd be interested to see if the edge manual install does the same on yours. Of course this does require more space as it installs to /opt and is bit more of a permanent solution.

1 Like

Hello my friends,

I'm trying to install AdguardHome for my OpenWrt, but I'm struggling using making it work at all. I followed the wiki page using the opkg package. should I install this as manually?

My original setup at home was a router + raspberry pi (pihole) . Making the Pihole to control DNS + unbound + DHCP and is a nice setup.

But now I moved and only have 1 openwrt and I want to add adguard but I can't make it work. This guide will be better for me?

My idea is to have adguard to take control of the DNS and DHCP instead Openwrt, is that a good idea? or any modern guide or what I should do?

I'm having those issues, yes please make a guide! I thought I was making something wrong. or where I should start?

Both the wiki and the manual version require some scripting to get AGH properly installed.

The manual version i detali in the first post on this thread is the one i recommend IF you have enough space on your router to install the full version to /opt/

In order for you to make AGH take over DNS and DHCP you will have to do something slightly different during the install. You would also be advised to setup your pc/laptop to use a static address with external dns during this.

See here

1 Like

Thank you. When possible I will try changing start to 99 to see it makes any difference.

I'm using the opkg version because I do my own builds to include everything I need in the sysupgrade image. This make OpenWrt upgrade process much easier (I do not have to reinstall anything after the upgrade and all settings are preserved).

1 Like

Thank you @mercygroundabyss

I have 202.28 mb ram available of 256 MB
and 256 mb nand on the edgerouter-x is enough? I think I have available close to 200 mb free of disk.

should I go for the full version? or the package is good enough? I just want to get it work either way. My goal is also to add wireguard server eventually but I prefer get adguardhome working it first.

I don't mind to use the DHCP of Openwrt I just want to make the adguard works, Im starting to learn linux and homelab as hobby, so my network is simple, I prefer to use Openwrt to do the dhcp then. I've been trying a few days to install adguard and I'm having similar issues from this thread. Like list reseting, getting certificates issues, etc.

I appreciate the time you put on writing guide and helping others!

thank you

1 Like

The wiki lists the pre-reqs however:

  • Your ram should be ok as long as you dont go overboard on filters/blocklists.
  • Your diskspace is abit low but workable. You require a minimum of 100mb realistically.

I did a space update a little while ago.

I still prefer the edge manual install vs the opkg version. Edge gets faster updates for stuff that gets broken. Once they hit a more stable cadence then I'd possibly tell people to use the stable tree.

DHCP is bit trickier. OpenWrt DHCP is much more mature and more suited to complex networks. I'm not sure AGH teams DHCP implementation is as robust. If you have a simple network its certainly worth a go and it will simplify things as you can just disable dnsmasq, odhcpd and replace it with AGH DHCP.

Its what i jokingly call my open heart surgery process. As we move dnsmasq, and insert AGH instead.
I'd be happier if we could do a lego brick style replacement where AGH just swaps in for DNS/DHCP completely but due to the many different setups that are possible, the cut and splice we do to just replace DNS is somewhat safer.

Certs aren't really required. However you can use Nginx to proxy so you can use your OpenWrt cert for both AGH and Luci. (It is also possible to do LetsEncrypt cert for Luci but that is documented elsewhere. I have done it with a dynamic dns provider but involves using a DNS challange method. look for acme There is also a Luci plugin for it.

Package name Version Size (.ipk) Description
acme 3.0.1-1 ~52.2 KB A client for issuing Letsencrypt certificates. Remove…
acme-dnsapi 3.0.1-1 ~133.4 KB This package provides DNS API integration for ACME (Letsencrypt) client. Remove…
luci-app-acme git-21.173.22454-e265ea3 ~3.4 KB ACME package - LuCI interface Remove…
luci-i18n-acme-en git-22.085.24559-f4a1fe6 ~1.2 KB Translation for luci-app-acme - English

@mercygroundabyss I forgot to mention that even using all recommended settings in Wiki plus the change above, local DNS resolution in OpenWrt still doesn't work unless is added to the bind_hosts section in /etc/adguardhome.yaml:


It seems that the local name resolution in OpenWrt is in fact pointing to, see below:

root@router:/# nslookup www.google.com

Non-authoritative answer:
Name:   www.google.com

Non-authoritative answer:
Name:   www.google.com
Address: 2001:4860:4802:32::78


It is strange, since it seems to be still looping back to AGH (127.0.01:53) even after I removed network.lan.dns=''...

Is it something expected?

Do it this way instead. I need to do some testing but dont have time right now. But my original assumption about that option was incorrect.

I was originally using split DNS back in my early testing and it was later after i found the issue with NTP that i dropped that part.

Also you should check what your upstream DNS for OpenWrt is vs what AGH is using. If you didnt use my DNS script it should be your local ISP dns via WAN dhcp. If you did then it should be Cloudflare's service.

1 Like


a theoretical question pls:

current approach is to make AGH the primary DNS resolver and leave dnsmasq as local (DHCP/)DNS only by binding it to port 54 and setting AGH as its upstream server. so technically any client DNS request would go to AGH first then if it is a public request go to public DNS upstream via AGH filtered, or if it is a local query bounce back to dnsmasq (e.g. local DHCP clients PTR query).

to resolve the race condition if AGH using DoH which requires TLS which requires correct timestamp hence NTP sync should happen first, with a domain rule can be set to use a traditional DNS upstream provider, that's ok.

but this does not solve the problem when AGH is not available for whatever reason, e.g. if NTP sync happens at very early stage might AGH not started yet so will fail regardless the domain rule.

so my theoretical question is what if keep dnsmasq as primary resolver, use two upstream resolvers AGH and a public traditional one (non-DoH), and set strictorder on. AGH would be first upstream server, if it is up and running should do all the filtering magic, answer all queries as expected; if it is not then query the secondary public resolver?