Update notifications¶
Difficulty: Easy Tested v0.18.5.3
Monero, P2Pool and XMRig aren't installed through apt, so unattended-upgrades never updates them. This guide adds a small script that checks once a day whether a newer version is out, and whether the ban list changed. It shows the result when you log in:
MoneroBox: updates available
monerod 0.18.5.3 -> 0.18.6.0 (guide 2.1, Upgrade)
Ban list changed upstream: Monero Research Lab spy node ban list version 3, 2026-11-02 (bonus: Monero ban list)
When everything is current, it shows nothing.
It only notifies
The script never downloads or installs a new version. You still upgrade by hand, following each guide's Upgrade section, so you verify every release yourself.
What it checks¶
| Software | Compared with | Only if installed |
|---|---|---|
| monerod (2.1) | Latest release on GitHub | Yes |
| P2Pool (3.1) | Latest release on GitHub | Yes |
| XMRig (3.2) | Latest release on GitHub | Yes |
| monero-lws (Light wallet server) | Highest release tag on GitHub | Yes |
| Ban list (Monero ban list) | The published file, byte for byte | Yes |
Everything installed with apt, including Tor and the system itself, is already covered by Ubuntu's own login message ("X updates can be applied") and by unattended-upgrades from 1.5.
Privacy
Every lookup goes through Tor. GitHub never sees your IP address, so nobody can link your home connection to the list of Monero software you run. The script only reads public pages: it sends no data about your node.
Requirements¶
- 1.6 Privacy (Tor): Tor running
gitandcurl, both installed in 1.4
Install the script¶
Paste the whole script:
#!/usr/bin/env bash
# MoneroBox: check for new versions of the guide's software and the ban list.
# Writes a short report that the login message shows. All lookups go through
# Tor, so GitHub never learns your IP address or which software you run.
#
# Environment (all optional):
# MONEROBOX_PROXY proxy for lookups (default: socks5h://127.0.0.1:9050;
# set it to an empty value to connect directly)
# MONEROBOX_BAN_LIST your copy of the ban list (default: /data/monero/ban_list.txt)
# STATE_DIRECTORY where the report goes (set by systemd)
set -uo pipefail
PROXY=${MONEROBOX_PROXY-socks5h://127.0.0.1:9050}
BAN_LIST=${MONEROBOX_BAN_LIST:-/data/monero/ban_list.txt}
BAN_LIST_URL=https://raw.githubusercontent.com/Boog900/monero-ban-list/main/ban_list.txt
STATE_DIR=${STATE_DIRECTORY:-/var/lib/monerobox-updates}
REPORT=$STATE_DIR/status
export GIT_TERMINAL_PROMPT=0 HOME=$STATE_DIR
updates=()
errors=()
# curl and git, both through the proxy when one is set
fetch() {
curl -fs --max-time 90 --retry 2 ${PROXY:+--proxy "$PROXY"} "$@"
}
git_remote() {
git ${PROXY:+-c http.proxy="$PROXY"} ls-remote --tags --refs "$@"
}
# "v4.18" -> "4.18.0", "v0.18.5.3-release" -> "0.18.5.3"
normalize() {
local v=${1#v}
v=${v%%[-+ ]*}
[[ $v =~ ^[0-9]+\.[0-9]+$ ]] && v=$v.0
printf '%s' "$v"
}
# True when $2 is a newer version than $1
is_newer() {
[[ $1 != "$2" && $(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1) == "$2" ]]
}
# Latest release tag, read from GitHub's /releases/latest redirect (no API, no rate limit)
latest_release() {
local url
url=$(fetch -o /dev/null -w '%{redirect_url}' "https://github.com/$1/releases/latest") || return 1
[[ $url == */tag/* ]] || return 1
printf '%s' "${url##*/tag/}"
}
# Highest vX.Y.Z tag, for projects that tag releases without publishing them
latest_tag() {
git_remote "https://github.com/$1.git" 2>/dev/null \
| sed -n 's#.*refs/tags/##p' \
| grep -E '^v?[0-9]+\.[0-9]+(\.[0-9]+)?$' \
| sort -V | tail -n1
}
# check NAME COMMAND REPO SOURCE GUIDE
# COMMAND prints the installed version; SOURCE is "release" or "tag"
check() {
local name=$1 cmd=$2 repo=$3 source=$4 guide=$5 installed latest
command -v "${cmd%% *}" >/dev/null 2>&1 || return 0 # not installed here: skip
installed=$($cmd 2>/dev/null | grep -oE 'v?[0-9]+\.[0-9]+(\.[0-9]+)*' | head -n1)
if [[ -z $installed ]]; then
errors+=("$name: couldn't read the installed version")
return
fi
if [[ $source == release ]]; then
latest=$(latest_release "$repo")
elif command -v git >/dev/null 2>&1; then
latest=$(latest_tag "$repo")
else
errors+=("$name: git isn't installed (sudo apt install git)")
return
fi
if [[ -z $latest ]]; then
errors+=("$name: couldn't reach github.com/$repo (is Tor running?)")
return
fi
if is_newer "$(normalize "$installed")" "$(normalize "$latest")"; then
updates+=("$(printf '%-12s %s -> %s (%s)' "$name" "$(normalize "$installed")" "$(normalize "$latest")" "$guide")")
fi
}
check monerod "monerod --version" monero-project/monero release "guide 2.1, Upgrade"
check P2Pool "p2pool --version" SChernykh/p2pool release "guide 3.1, Upgrade"
check XMRig "xmrig --version" xmrig/xmrig release "guide 3.2, Upgrade"
check monero-lws "monero-lws-daemon --version" vtnerd/monero-lws tag "bonus: Light wallet server"
mkdir -p "$STATE_DIR"
# Ban list: compare your copy with the published one, byte for byte
if [[ -r $BAN_LIST ]]; then
remote=$(mktemp "$STATE_DIR/.ban_list.XXXXXX")
if fetch -o "$remote" "$BAN_LIST_URL"; then
if ! cmp -s "$BAN_LIST" "$remote"; then
header=$(sed -n '1s/^# *//p' "$remote")
updates+=("$(printf '%-12s changed upstream%s (bonus: Monero ban list)' "Ban list" "${header:+: $header}")")
fi
else
errors+=("Ban list: couldn't download it (is Tor running?)")
fi
rm -f "$remote"
fi
# Write the report atomically, so a login never sees half of it
tmp=$(mktemp "$STATE_DIR/.status.XXXXXX")
{
if ((${#updates[@]})); then
echo "MoneroBox: updates available"
printf ' %s\n' "${updates[@]}"
fi
if ((${#errors[@]})); then
echo "MoneroBox: some update checks failed"
printf ' %s\n' "${errors[@]}"
fi
} > "$tmp"
chmod 644 "$tmp"
mv -f "$tmp" "$REPORT"
if [[ -s $REPORT ]]; then cat "$REPORT"; else echo "MoneroBox: everything is up to date"; fi
Make it executable:
Run it daily¶
A systemd timer runs the check once a day, and 15 minutes after each boot. The service runs as a temporary, unprivileged user that systemd creates for each run. It can read your installed programs and the ban list, and write only its own report.
# MoneroBox: daily check for new versions
# /etc/systemd/system/monerobox-update-check.service
[Unit]
Description=MoneroBox update check
Wants=network-online.target
After=network-online.target tor.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/monerobox-update-check
DynamicUser=yes
# Read access to /data/monero/ban_list.txt
SupplementaryGroups=monero
StateDirectory=monerobox-updates
# Hardening
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
NoNewPrivileges=yes
# MoneroBox: schedule for the update check
# /etc/systemd/system/monerobox-update-check.timer
[Unit]
Description=Daily MoneroBox update check
[Timer]
OnBootSec=15min
OnCalendar=daily
RandomizedDelaySec=2h
Persistent=true
[Install]
WantedBy=timers.target
RandomizedDelaySec spreads the check over two hours, so it doesn't happen at the same time every day.
Show it at login¶
Ubuntu builds its login message from the scripts in /etc/update-motd.d/. Add one that prints the latest report:
#!/bin/sh
# MoneroBox: show the result of the last update check at login
report=/var/lib/monerobox-updates/status
[ -f "$report" ] || exit 0
# An empty report means everything is up to date: print nothing
if [ -s "$report" ]; then
echo
cat "$report"
fi
# Warn when the daily check has stopped running
age=$(( $(date +%s) - $(stat -c %Y "$report") ))
if [ "$age" -gt 259200 ]; then
[ -s "$report" ] || echo
echo "MoneroBox: the last update check ran $(( age / 86400 )) days ago. Check: systemctl status monerobox-update-check.timer"
fi
Validation¶
Run the check once by hand, and read its output:
sudo systemctl start monerobox-update-check
sudo journalctl -u monerobox-update-check -n 10 --no-pager
monerobox-update-check[4321]: MoneroBox: everything is up to date
systemd[1]: monerobox-update-check.service: Deactivated successfully.
Check that the timer is scheduled:
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2026-10-17 01:12:40 UTC 15h left Fri 2026-10-16 09:30:02 UTC 3min ago monerobox-update-check.timer monerobox-update-check.service
Log out and in again. When an update is available, it appears at the end of the login message.
Troubleshooting¶
couldn't reach github.com/… (is Tor running?)
systemctl is-active tor@defaultmust printactive. Tor needs a minute after a reboot; the next check retries anyway.- Test Tor by hand:
curl --socks5-hostname 127.0.0.1:9050 -sI https://github.com | head -1should print an HTTP status line. - GitHub occasionally refuses connections from Tor exit relays. If one check fails, the next one usually works.
couldn't read the installed version
The program is installed, but its --version output looks different than expected, for example after a big upstream change. Run the command from the table by hand (for example p2pool --version) and open an issue with the output.
The login message doesn't show the report
- The report only has content when there's something to say. Check it directly:
sudo cat /var/lib/monerobox-updates/status. - Check that the motd script is executable:
ls -l /etc/update-motd.d/95-monerobox-updates.
Uninstall¶
sudo systemctl disable --now monerobox-update-check.timer
sudo rm /etc/systemd/system/monerobox-update-check.{service,timer}
sudo rm /usr/local/bin/monerobox-update-check /etc/update-motd.d/95-monerobox-updates
sudo systemctl daemon-reload
sudo rm -rf /var/lib/private/monerobox-updates /var/lib/monerobox-updates