Adblock-lean: set up adblock using dnsmasq blocklist

On one hand, I'm happy that we didn't implement this option for nothing (i.e. that someone is actually using it), on the other hand - updating anything on a complete autopilot is risky, especially so updating an app which manages a critical system service (i.e. DNS resolver on a router). Also keep in mind that adblock-lean is a small indie project, maintained by a couple of guys (myself and @Lynx). While we are doing our best to test before making a new release, we are just two people and at worst no one else, at best a couple volunteers help us with testing. And while we have invested (and continuing to invest) a lot of effort in error checking and handling, fallback mechanisms etc, there is no way for us to guarantee that an update will not break anything.

Yeah, this is another thing I was thinking about - that the user should explicitly enable this functionality, not just provide a path. So in the meantime, I replaced the option PERM_BLOCKLIST_IS_MAIN with the option PERM_BLOCKLIST_MODE. This is the description which I'm planning to include for this option:

# Governs whether and how permanent blocklist is used
# 'disable' (default): permanent blocklist will not be used. The blocklist file will be stored on the ramdisk.
# 'manual': directory specified in PERM_BLOCKLIST_DIR will be checked for file named 'abl-blocklist' (with or without extension '.gz' or '.zst') -
#   if found, that blocklist will be loaded at boot (rather than downloading, processing and loading a new blocklist)
#   but adblock-lean will not create or update that file (useful to prevent flash wear, e.g. when the permanent blocklist is stored on the built-in flash of a router).
#   If not found, adblock-lean will act as if mode is 'disable'.
# 'main': adblock-lean will use the directory specified in PERM_BLOCKLIST_DIR to store and update the blocklist file
#   and no additional blocklist will be stored on the ramdisk.
#   If the directory is inaccessible, adblock-lean will fall back to using the ramdisk.

(this is a copy-paste from the comment which I'm planning to attach to the option in the config file)

Of course, I implemented handling of invalid/fault conditions, including when PERM_BLOCKLIST_MODE is set to manual or main but PERM_BLOCKLIST_DIR is not set or does not exist, as well as required addnmount entries checking and creation etc. There is a lot of logic to handle, but hopefully everything is covered at this point.

Again, I agree - this is the best idea I could come up with as well.

This is the exact thing I don't think is possible. Putting aside the question of how to re-define dnsmasq instances on-the-fly (as those are normally defined in /etc/config/dhcp which is typically stored on the flash and rewriting that file frequently would accelerate flash wear), the immediate show-stopper is that a second instance of dnsmasq can not listen on the same port and on the same network interface. If you try to make it (I just tried), it will print this error:

dnsmasq: failed to create listening socket for 192.168.1.101: Address in use

and exit.

So I simply do not see a way to start loading the blocklist into instance X, start instance Y in parallel, and kill that instance when instance X is done loading the blocklist. Either X or Y will die with an error at some point.

1 Like

Honestly, this is too complicated for my simple brain :slight_smile:

That said, I've been thinking about implementing support for using actually different blocklists for different dnsmasq instances (thus enabling similar functionality). While this is also a complex thing to do, at least I can see a way forward with it.

Being a fan of KISS, I start my private adblock on very first boot (after flash) using a default, minimal blocklist, included in custom image ( or package).
On first blocklist update, this permanently stored blocklist is replaced.

Thus, only in the time period between boot and first blocklist update afterwards, system is using a somewhat "out of date" blocklist.
Assuming rather infrequent boots, this should not be an issue for long term ops.

Goes to show without saying that I, and many, greatly appreciate the work and thank you for it even if unspoken at times. Seriously, thank you for the work. :grin:

It is indeed risky in that sense, but for me, that time is when I’ll be physically available in the place as that’s when I do visit. I also regularly visit the forums so I know if some thing is off or needs to be done. It’s just a convenient thing for me that unless there’s an issue, then I won’t even have to spend time to ssh to the router nor even think about it when I visit.

This is good and very understandable. I can only imagine how back and forth the work on the code is just to a handle a single logic more so if it’s a cascading logic, so again, greatly appreciate the work you do on that front.

1 Like

Hi,

I am strongly in favor of this.

Sorry that I have not taken time to digest it all and provide better feedback. Here are a couple thoughts. Some is a repeat of your proposal but omissions of other parts are not meant as negative feedback of other parts of your proposal.

Have the option to permanently store a blocklist and use it on boot.
Have the option to automatically update the permanent blocklist and define the replacement schedule, with the ability to set a specific time of day and which days of week or month etc. (easy with cron job?) Set up this logic to be simple and aimed at the existing crontab capabilities. Keep thundering herd protections or optionally toggle that off.

Have basic checks that a replacement list is sane to use, like number of list items before writing to storage. This is probably exactly as you already do. Keep previous list on any update failure. Be able to log success or failure and maybe list count etc.

I think enabling custom times for the update may be a suitable solution for the service outage for a lot of home use cases if for example set to human sleep times and avoiding relevant home-lab needs.

I think each ideas should be weighed in its effect on simplicity, efficiency, speed, feature, security etc keeping in line with the various aspects of “lean”.

Let me know if you want clarification on any of my thoughts on this new feature.

Last but NOT least, Thank You to maintainers and contributors. It has made this part of OpenWrt better for many.

1 Like

Another thought…

Be able to choose between:

  • Daily Fetching and using an updated list in RAM as is current practice with the addition of a updated permanent list as boot/fallback, updated on a user defined schedule. The permanent list could come from the currently loaded list. Don’t fetch a new remote list on boot / power cycle / dnsmasq restart.
  • Only load/use the permanent list with its update schedule.
  • Don’t use a permanent list.
  • Option to fail-over to option 2 if option 1 is set and fails.
  • Option to fail-over to live fetching of remote list if option 2 is set and fails.

With ( so far ) reliable USB storage, I see myself using option 2 with a daily update of the permanent list on USB storage but would like fail-over to fetching the remote list on boot / restart if loading the permanent list fails. I’m not sure daily updates are even needed for my simple use case / threat model and might consider biweekly updates or something like that.

Maybe with modular code, functions etc, I’m hoping much of the fallback can be done with calls to routines for the other option, maybe with adding a minimal amount of parameters and logic.

This all just some ideas and not a feature request.

1 Like

Thank you for your suggestions. I'm looking into implementing support for separate update schedule of the permanent blocklist but not sure this option will make it into the next release.

1 Like

hi,

is that package can be used instead of http-dns-proxy or dnsproxy, adguardhome etc and even banip or it is an adding to all of those?
thanks

adblock-lean can be used instead of adguardhome for network-wide adblocking. The other 3 packages you mentioned perform tasks which adblock-lean does not perform but you can use adblock-lean in addition to those packages.

1 Like

A short update about adblock-lean development. During the past few weeks, I've been working on implementing support for persistent blocklist, as discussed above. This turned out to be a much bigger task than I anticipated and required a lot of code restructuring and logic changes. In addition, I rewrote the message logging and printing code and started to prepare the code for future support of multiple blocklists. So (provided that @Lynx accepts the PR) the upcoming release is going to feature more code changes than any other in the history of this project. At this point the implementation is nearly complete, except I also want to implement a hotplug script which will react to storage device disconnections and if the disconnected device is the one where the persistent blocklist is stored, automatically restart adblock-lean (otherwise adblocking will stop working on dnsmasq restart). The PR is here.

P.s. I wonder whether anyone here has configured adblock-lean to use any compression utility which is not the default (gzip)? A few months ago we added support for zstd and pigz, both of which support multithreaded compression (decompression is still single-threaded though), and for GNU gzip. So with zstd and pigz, in theory this should be faster for a similar compression ratio. We also added options for the user to specify arguments to pass to the compression utility, so the compression level is also configurable. In practice, I don't think that this is that much faster (maybe a couple of seconds on a multi-core CPU), and I don't think that tweaking the compression level is very useful when speaking of files sized a single-digit number of MB. While the implementation does take some lines of code. And above all, I'm skeptical that many people are using this feature. So I'd like to get a bit of statistics on this and if nobody is using this feature, perhaps remove it.

Hey all, great project. I recently installed this on my router, and it works great. I am using this in place of using Adguard Home w/ a Pi Zero (now I can use my Pi Zero for something else!)

I do have some questions, however:

  1. With AGH, I was using Quad9 as my upstream DNS server. Should I still be able to follow these instructions and have it work with adblock-lean? https://docs.quad9.net/Setup_Guides/Open-Source_Routers/OpenWrt_LuCi/

  2. Is there a simple way to see what urls have been successfully blocked by adblock-lean?

Thanks.

Absolutely - you can use Quad9 or anything else like Cloudflare Family filter.

At present adblock-lean does not offer a way to show what has been blocked or not. I believe it would be possible to do so because dnsmasq itself allows logging all DNS queries.

Such monitoring would enable some insight to be obtained about blocking of queries.

Such such monitoring would also enable a nerd to monitor DNS queries associated with said nerd’s house guests.

2 Likes

Just insert the following line into end of /etc/dnsmasq.conf on your openwrt device:

log-queries

Then you need to analyze the system log. No urls logged, of course, but domain names, only.

You might also insert

log-facility=/tmp/dnsmasq.log

into dnsmasq.conf. BUT THEN TAKE CARE OF TRUNCATION OF THIS LOG FILE, not to fill up /tmp

2 Likes

Thank you as always for the hard work and continued support on the project!

Just the default on my installs. It’s all good for my use case anyway so I don’t think I’ll look into more than the default on this.

1 Like

I've been giving this a go, I was using ControlD but was tired of manually updating folders so I've installed this and it's running great.

I'm not that experienced so this one could be a skill issue on my part; I'm attempting to use cbuijs Hagazi Combos but I'm experiencing I'm assuming a failsafe stating I'm using the wrong lists due to a Suffix -onlydomains being missing.

I'm not sure if I can posts links but here it is: https://github.com/cbuijs/hagezi/blob/main/combo/alt-suggested/domains.top-n

Anything I can do?

Hi, Hagezi publishes two types of lists under the 'wildcard' directory. One type is compatible with adblock-lean, the other one isn't. The error message you are seeing is the result of us adding a check to make sure that the user is not trying to use the wrong type. Turns out that the check we implemented didn't take into account the possiblity of another repository distributing modified Hagezi lists with different file naming. So here you have a false positiive. I.e. the list you are trying to use is OK but the URL incorrectly triggers this error. We will fix this in the next release.

For now, you can either use the original Hagezi lists, or you can modify the script (located at /usr/lib/adblock-lean/abl-process.sh). If you want to modify the script, the line to modify is 843:

Replace this line with

[ -z "${bad_hagezi_urls}" ] || true ||

(copy-pase exactly this and replace the whole line)

1 Like
Working Details

Downloading and processing blocklist parts (max parallel jobs: 2).
Not using any allowlist for blocklist processing.
No local blocklist identified.
Processing raw blocklist: https://raw.githubusercontent.com/cbuijs/hagezi/refs/heads/main/combo/alt-suggested/domains.top-n
Processing raw blocklist: https://raw.githubusercontent.com/hagezi/dns-blocklists/refs/heads/main/wildcard/nsfw-onlydomains.txt
Successfully processed list: https://raw.githubusercontent.com/hagezi/dns-blocklists/refs/heads/main/wildcard/nsfw-onlydomains.txt [ 1.01 MiB - 66,878 lines ]
Successfully processed list: https://raw.githubusercontent.com/cbuijs/hagezi/refs/heads/main/combo/alt-suggested/domains.top-n [
1.72 MiB - 82,096 lines ]
Successfully generated preprocessed blocklist file with 148,974 entries.

Fantastic! appreciate the spoonfeed and now all is up and running but the failsafe is excellent though because I did use the wrong file initially with Hagazi Ultimate :joy:

Thanks!

2 Likes
[6 Mar 2026, 16:41:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:41:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:41:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:42:00 GMT+8] cron.err: crond[1919]: wakeup dt=60
[6 Mar 2026, 16:42:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:42:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:42:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:43:00 GMT+8] cron.err: crond[1919]: wakeup dt=60
[6 Mar 2026, 16:43:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:43:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:43:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:44:00 GMT+8] cron.err: crond[1919]: wakeup dt=60
[6 Mar 2026, 16:44:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:44:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:44:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:45:00 GMT+8] cron.err: crond[1919]: wakeup dt=60
[6 Mar 2026, 16:45:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:45:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:45:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:46:00 GMT+8] cron.err: crond[1919]: wakeup dt=60
[6 Mar 2026, 16:46:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:46:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:46:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:47:00 GMT+8] cron.err: crond[1919]: wakeup dt=60
[6 Mar 2026, 16:47:00 GMT+8] cron.err: crond[1919]: file root:
[6 Mar 2026, 16:47:00 GMT+8] cron.err: crond[1919]:  line /usr/bin/wireguard_watchdog >> /etc/crontabs/root
[6 Mar 2026, 16:47:00 GMT+8] cron.err: crond[1919]:  line RANDOM_DELAY=1 /etc/init.d/adblock-lean start 1>/dev/null
[6 Mar 2026, 16:48:00 GMT+8] cron.err: crond[1919]: wakeup dt=60

Hi, after upgrading from 24.10.5 to 25.12.0, my cron job log spammed with this error.

Any idea to fix this?

My device is cr6608

 "kernel": "6.12.71",
        "hostname": "c",
        "system": "MediaTek MT7621 ver:1 eco:3",
        "model": "Xiaomi Mi Router CR6608",
        "board_name": "xiaomi,mi-router-cr6608",
        "rootfs_type": "squashfs",
        "release": {
                "distribution": "OpenWrt",
                "version": "25.12.0",
                "firmware_url": "https://downloads.openwrt.org/",
                "revision": "r32713-f919e7899d",
                "target": "ramips/mt7621",
                "description": "OpenWrt 25.12.0 r32713-f919e7899d",
                "builddate": "1772496855"

Check your /etc/config/system and look for the option cronloglevel and if that was set before with a different level then change it to ‘7’ like mentioned in the release notes above.

I encountered something similar before when I did test RC2 so that’s one of the things I updated for the cron.err lines in the log back then.

EDIT: Referring to System configuration /etc/config/system doc the default used to be ‘5’

1 Like

I’ve changed cronloglevel to 5 from 7..will see the effect.

My other router, Mi ax3000t, doesnt acting like this…only my cr6608.