Update Pi-hole's lists from remote sources
User-created entries will not be touched and those removed from the remote list will be disabled instead.
If you're not using remote lists like the ones mentioned above then this script will be useless to you - Pi-hole already updates the lists weekly automatically.
- Pi-hole v5+ installed (fresh install preferred)
- php-cli >=7.0 and a few extensions (
sudo apt-get install php-cli php-sqlite3 php-intl php-curl)
- systemd is optional but recommended
Docker users - look below.
This command will install this script to
wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | sudo bash
Alternatively you can clone this repo and
sudo bash install.sh.
If systemd is available this script will also add service and timer unit files to the system, otherwise a crontab entry in
/etc/cron.d/pihole-updatelists will be created.
If for some reasons the install script does not copy service and timer files while your distro has systemd scheduler available you can force the installation by passing
systemd as a parameter to the install script - modifying the install command above with
sudo bash /dev/stdin systemd instead.
Note that in most cases you will be able to execute this script globally as
pihole-updatelists command but some will require you to add
$PATH or execute it via
This script does nothing by default (except running
pihole updateGravity), you have to configure it.
You can quickly update the script with
sudo pihole-updatelists --update which checks for script difference and re-runs the install script when needed.
Disable default gravity update schedule
If you don't plan on updating adlists or want to keep Pi-hole's gravity update schedule you should skip this section and set
UPDATE_GRAVITY=false in the configuration file.
You should disable entry with
pihole updateGravity command in
/etc/cron.d/pihole as this script already runs it:
sudo nano /etc/cron.d/pihole
# before this line (numbers might be different):
#49 4 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log
Alternatively, the following
sed command will disable the same entry:
sudo sed -e '/pihole updateGravity/ s/^#*/#/' -i /etc/cron.d/pihole
You might have to do this after each Pi-hole update.
Migrating lists and domains
If you already imported any of the remote lists manually you should migrate their entries to allow the script to disable them in case they are removed from the remote list.
If you used pihole5-list-tool to import adlists and whitelist you can use these commands to do this quickly:
sudo sqlite3 /etc/pihole/gravity.db "UPDATE adlist SET comment = 'Managed by pihole-updatelists' WHERE comment LIKE '%Firebog |%' OR comment LIKE '%[ph5lt]'" sudo sqlite3 /etc/pihole/gravity.db "UPDATE domainlist SET comment = 'Managed by pihole-updatelists' WHERE comment LIKE '%AndeepND |%' OR comment LIKE '%[ph5lt]'"
(code up to date as of pihole5-list-tool 0.6.0)
Alternatively, some manual work is required - pick one:
- Manually modify comment field of all imported domains/adlists to match the one this script uses (see
COMMENTvariable in Configuration section) (recommended but might be a lot of work)
- Manually delete all imported domains/adlists from the web interface (might be a lot of work)
- Wipe all adlists and domains (not recommended but fast - use this if you want to start fresh)
- backup your lists and custom entries (write them down somewhere, do not use the Teleporter)
- run the following commands:
sudo sqlite3 /etc/pihole/gravity.db "DELETE FROM adlist" sudo sqlite3 /etc/pihole/gravity.db "DELETE FROM adlist_by_group" sudo sqlite3 /etc/pihole/gravity.db "DELETE FROM domainlist" sudo sqlite3 /etc/pihole/gravity.db "DELETE FROM domainlist_by_group"
- keep reading and configure the script then run
sudo pihole-updateliststo finish up
- (only when
Install with Docker
Follow the official instructions and add a volume for
If you need to pull a specific version of Pi-hole image you have no other choice but to use custom Dockerfile.
Please note that if you're setting this up for the first time (
gravity.db is missing) you will have to restart the container after initial launch is complete, otherwise you will have to wait for the scheduled run to fetch the lists.
You could also execute
/usr/bin/php /usr/local/sbin/pihole-updatelists --config=/etc/pihole-updatelists/pihole-updatelists.conf in the container instead if you prefer to not restart the container.
Using custom image
jacklul/pihole:latest image instead of
Using official image
If you don't want to use my image you can write custom
FROM pihole/pihole:latest RUN apt-get update && apt-get install -Vy wget php-cli php-sqlite3 php-intl php-curl RUN wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | bash /dev/stdin docker
Then build your image locally and use that image in your
docker-composer.yml or launch command line.
You will have to update your local image manually each time update is released.
docker-compose.yml file should look similar to this:
version: "3" services: pihole: container_name: pihole image: jacklul/pihole:latest ports: - "53:53/tcp" - "53:53/udp" - "67:67/udp" - "80:80/tcp" environment: TZ: 'America/Chicago' volumes: - './etc-pihole/:/etc/pihole/' - './etc-dnsmasq.d/:/etc/dnsmasq.d/' - './etc-pihole-updatelists/:/etc/pihole-updatelists/' cap_add: - NET_ADMIN restart: unless-stopped
(for more up to date
docker-compose.yml see pi-hole/docker-pi-hole)
If you already have existing
gravity.db you should also check out Migrating lists and domains section, keep in mind that you will have to adjust paths in the commands mentioned there.
Default configuration file is
sudo nano /etc/pihole-updatelists.conf
|ADLISTS_URL||" "||Remote list URL containing list of adlists to import
URLs to single adlists are supported but it might be better if you add them manually
|WHITELIST_URL||" "||Remote list URL containing exact domains to whitelist|
|REGEX_WHITELIST_URL||" "||Remote list URL containing regex rules for whitelisting|
|BLACKLIST_URL||" "||Remote list URL containing exact domains to blacklist
This is specifically for handcrafted lists only, do not use regular blocklists here!
|REGEX_BLACKLIST_URL||" "||Remote list URL containing regex rules for blacklisting|
|COMMENT||"Managed by pihole-updatelists"||Comment string used to know which entries were created by the script
You can still add your own comments to individual entries as long you keep this string intact
|GROUP_ID||0||Assign additional group to all inserted entries, to assign only the specified group (do not add to the default) make the number negative
Multiple groups are not supported
|PERSISTENT_GROUP||true||Makes sure entries have the specified group assigned on each script run
This does not prevent you from assigning more groups through the web interface but can remove entries from the default group if GROUP_ID is a negative number
When disabled but an entry has no groups assigned and is about to be enabled then it will be re-added to the groups it's supposed to be in
WARNING: This option might be buggy when running multiple different configurations with same lists
|REQUIRE_COMMENT||true||Prevent touching entries not created by this script by comparing comment field
|UPDATE_GRAVITY||true||Update gravity after lists are updated? (runs
|VERBOSE||false||Show more information while the script is running|
|DEBUG||false||Show debug messages for troubleshooting purposes
If you're having issues - this might help tracking it down
|DOWNLOAD_TIMEOUT||60||Maximum time in seconds one list download can take before giving up
You should increase this when downloads fail because of timeout
|IGNORE_DOWNLOAD_FAILURE||false||Ignore download failures when using multiple lists
This will cause entries from the lists that failed to download to be disabled
|LOCK_FILE||"/var/lock/pihole-updatelists.lock"||Process lockfile to prevent multiple instances of the script from running
You shouldn't change it - unless
|LOG_FILE||" "||Log console output to file
In most cases you don't have to set this as you can view full log in the system journal
|GIT_BRANCH||"master"||Branch to pull remote checksum and update from|
String values should be put between
" ", otherwise weird things might happen.
You can also give paths to the local files instead of URLs, for example setting
/home/pi/whitelist.txt will fetch this file from filesystem.
You can specify alternative config file by passing the path to the script through
pihole-updatelists --config=/home/pi/pihole-updatelists2.conf - this combined with different
COMMENT string can allow multiple script configurations for the same Pi-hole instance.
A more advanced way is to use sections in the configuration file:
(bottom of the file) [GroupA_adlists] WHITELIST_URL="https://raw.githubusercontent.com/you/adlists/master/my_whitelist1.txt" GROUP_ID=-1 COMMENT="pihole-updatelists - whitelist1" [GroupB_adlists] WHITELIST_URL="https://raw.githubusercontent.com/you/adlists/master/my_whitelist2.txt" GROUP_ID=-2 COMMENT="pihole-updatelists - whitelist2"
Configurations where one of the lists contains entries from the other are not officially supported but may work:
; When one of the lists contains entries from the other ; it's best to have it defined after the other one ; Group with ID=1 will use 'tick' list of adlists [GroupA_adlists] ADLISTS_URL="https://v.firebog.net/hosts/lists.php?type=tick" GROUP_ID=-1 COMMENT="pihole-updatelists - firebog (tick)" ; Group with ID=2 will use 'nocross' list of adlists [GroupB_adlists] ADLISTS_URL="https://v.firebog.net/hosts/lists.php?type=nocross" GROUP_ID=-2 COMMENT="pihole-updatelists - firebog (nocross)"
You will want to have a different
COMMENT value in each section, they have to be unique and one must not match the other!
Main configuration (the one without section header) is processed first, then the sections in the order of their appearance.
IMPORTANT: You can only use selected variables in sections:
ADLISTS_URL, WHITELIST_URL, REGEX_WHITELIST_URL, BLACKLIST_URL, REGEX_BLACKLIST_URL, COMMENT, GROUP_ID, PERSISTENT_GROUP, IGNORE_DOWNLOAD_FAILURE
Multiple list URLs
You can pass multiple URLs to the list variables by separating them with whitespace (space or new line):
If one of the lists fails to download nothing will be affected for that list type.
|https://v.firebog.net/hosts/lists.php?type=tick||https://firebog.net - safe lists only|
|https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt||https://github.com/anudeepND/whitelist - commonly whitelisted|
|https://raw.githubusercontent.com/mmotti/pihole-regex/master/regex.list||https://github.com/mmotti/pihole-regex - basic regex rules|
Please note that mmotti/pihole-regex list can sometimes block domains that should not be blocked - any false positives should be reported to the repository to be included in the whitelist (in that case you might consider adding that list to the
These can be used when executing
||Show help message, which is simply this list|
||Force gravity update to be skipped|
||Force lists reload to be skipped
Only if gravity update is disabled either by configuration (
||Turn on verbose mode|
||Turn on debug mode|
||Load alternative configuration file|
||Select git branch to pull remote checksum and update from
Can only be used with
||Update the script using selected git branch|
||Rollback script version to previous|
||Force update without checking for newest version|
||Automatically reply YES to all questions|
||Show script checksum (and also if update is available)|
Changing the schedule
By default, the script runs at random time (between 03:00 and 04:00) on Saturday, to change it you'll have to override timer unit file:
sudo systemctl edit pihole-updatelists.timer
[Timer] RandomizedDelaySec=5m OnCalendar= OnCalendar=Sat *-*-* 00:00:00
If systemd is not available you just modify the crontab entry in
14 6 * * 6 root /usr/local/sbin/pihole-updatelists
Running custom commands before/after scheduled run
Override service unit file:
sudo systemctl edit pihole-updatelists.service
[Service] Type=oneshot ExecStartPre=echo "before" ExecStartPost=echo "after"
If systemd is not available you just modify the crontab entry in
30 3 * * 6 root /home/pi/before.sh && /usr/local/sbin/pihole-updatelists && /home/pi/after.sh
You can use
; instead of
&& if you don't want the execution to stop on previous command failure.
Changing comment value after running the script
sudo sqlite3 /etc/pihole/gravity.db "UPDATE adlist SET comment = 'NEWCOMMENT' WHERE comment LIKE '%Managed by pihole-updatelists%'" sudo sqlite3 /etc/pihole/gravity.db "UPDATE domainlist SET comment = 'NEWCOMMENT' WHERE comment LIKE '%Managed by pihole-updatelists%'"
NEWCOMMENT with your new desired comment value. This assumes
Managed by pihole-updatelists is the old comment value, replace it with your old custom value when needed.
Custom comments for entries
If you wish to add custom comments to entries you can use the following file syntax:
example-domain.com # your comment
Which will cause
example-domain.com to have
comment set to
your comment | Managed by pihole-updatelists.
You can also add your comments directly through the Pi-hole's web interface by either appending or prepending the comment field for entries.
wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | sudo bash /dev/stdin uninstall
or remove files manually:
sudo rm -vf /usr/local/sbin/pihole-updatelists /etc/bash_completion.d/pihole-updatelists /etc/systemd/system/pihole-updatelists.service /etc/systemd/system/pihole-updatelists.timer /etc/cron.d/pihole-updatelists