Pihole Blocking Schedule

4 minutes read | 770 words

Aaruni Kaushik

Pi-hole

Pi-hole is a network wide adblocker service for Linux systems. It works as a DNS blackhole, that is, it blocks resolving DNS queries for certain addresses. So, it is able to block ads by refusing to resolve the address from which an ad must be loaded! Contrary to what the name might suggest, Pi-hole can run on almost any Linux box on almost any platform, not just a Raspberry-Pi computer. More information about Pi-hole can be found at their official website.

The Use Case

An interesting use case I came across recently wrt pihole is blocking things on a schedule: as a simple way to implement internet curfew for kids. Internet stops working every night, and starts working again every morning! While this is not possible with just the UI that pihole exposes, it does become possible if you set up things the right way, and then poke at the underlying gravity database manually.

WARNING : Bypassing the UI to manually change the database can have unintended side effects, and has no guardrails. Be careful with what you do. You have been warned.

Setup

Groups

Lets say you have only two categories of devices on your network : things with the default blocklist, and things which get everything blocked for them (effectively shutting down most internet). Pihole already ships with the default group. Make two more groups: one which resolves nothing, and another, which has no clients. It might look something like the image.

Groups configuration

Clients

Pi-hole identifies clients by its MAC address, while most smartphones today randomize their MAC address as a privacy measure. In my opinion, this measure is not required on your home network, where you’re most probably administering a Pi-hole, and is required for this method to work. So, just turn MAC randomization off for the home LAN.

Then, add the devices you want to block on a schedule to the Blacklist group.

Clients Configuration

RegEx

In the domain tab, add a regex filter to block everything. The rule I use for this is [:print:]*, which blocks URLs with any combination of printable characters, so, EVERYTHING. The active domain list should now contain this rule. Make sure that the group assignment is Empty, the group with no clients in it.

List Configuration

Pihole Config

I run my pihole on bare metal, and not in a container, so my database sits at /etc/pihole/gravity.db. If this is different for you, change it according to your setup.

Poking the Database

If your setup is the different than that in this post, then you will get slightly different output. But the principal still stands.

Lets first check that the database stores things in the expected way.

$ sudo sqlite3 /etc/pihole/gravity.db "SELECT * from 'group';"
0|1|Default|1633706065|1633706065|The default group
1|1|Blacklist|1676039947|1676039947|This groups resolves nothing
2|1|Empty|1676041237|1676041237|This group contains no clients
$ sudo sqlite3 /etc/pihole/gravity.db "SELECT * from 'domainlist';"
1|3|[:print:]*|1|1676040225|1676041244|Block *Everything*

Verify that your blocklist has the id 1, and your block regex has the id 1 (first entry in the output).

Now, to block everything for all clients in the Blacklist group, just set the filter to work on its group id (1 in our example).

sudo sqlite3 /etc/pihole/gravity.db "update domainlist_by_group set group_id=1 where domainlist_id=1"

You can now check in the UI that this has actually changed the group assignment of the filter in the Domains tab.

Now reload the lists in pihole, and the targeted devices will start being blocked. (Minor caveat, if the client devices cache their DNS, they will still be able to “resolve” things they remember for as long as the cache doesn’t expire. On my android phone, it takes a minute or so for this to happen.)

sudo pihole restartdns reload-lists

When you want to unblock these devices, set the blacklist to apply to the empty group again, and update the filter list. The UI will reflect this change, and the blacklist regex will be assigned to Empty.

$ sudo sqlite3 /etc/pihole/gravity.db "update domainlist_by_group set group_id=2 where domainlist_id=1"
$ sudo pihole restartdns reload-lists

Scheduling

To run these actions on a schedule, it’s quite easy to convince your favorite task scheduler to fire this script. An example to do this via systemd timers is provided below.

Blocking

/etc/systemd/system/pihole_block.service

[Unit]
Description=Start blocking on pihole

[Service]
Type=oneshot
ExecStart=/usr/bin/sqlite3 /etc/pihole/gravity.db "update domainlist_by_group set group_id=1 where domainlist_id=1"
ExecStartPost=/usr/local/bin/pihole restartdns reload-lists

/etc/systemd/system/pihole_block.timer

[Unit]
Description=Setup pihole blocking on a schedule

[Timer]
#Run at 10pm every day
OnCalendar=*-*-* 22:00:00
Persistent=true

[Install]
WantedBy=timers.target

Unblocking

/etc/systemd/system/pihole_unblock.service

[Unit]
Description=Start blocking on pihole

[Service]
Type=oneshot
ExecStart=/usr/bin/sqlite3 /etc/pihole/gravity.db "update domainlist_by_group set group_id=2 where domainlist_id=1"
ExecStartPost=/usr/local/bin/pihole restartdns reload-lists

/etc/systemd/system/pihole_unblock.timer

[Unit]
Description=Setup pihole unblocking on a schedule

[Timer]
#Run at 8am every day
OnCalendar=*-*-* 08:00:00
Persistent=true

[Install]
WantedBy=timers.target