Maintain and upgrade NetBox

Nightly backups you have actually restored once, an upgrade path you can roll back, the housekeeping job, log rotation, and a probe that tells you NetBox is alive.

intermediate~35 min hands-on
#netbox#backup#postgresql#systemd#upgrade#monitoring

Not validated end to end yet — be the first.Report a problem

Draft — not yet run end to end. This page was written but its author has not yet run it on a real machine. Commands may be wrong: read before you run, and tell us what breaks.

The gistWhat runs every night, and what you run on purpose
Scheduled on the server
NetBox server
02:30writesgit checkout restart
Nightly timer02:30 · netbox-backup.timer
Backup scriptpg_dump -Fc · tar
Backup directory
NetBox releasesgit tags ·
upgrade.shvenv · migrations · static
netbox · netbox-rqsystemctl restart

A timer dumps the database and archives the files into the backup directory; with the off-site option they are pushed elsewhere. An upgrade is a tag checked out from git, applied by upgrade.sh, verified by a probe on /api/status/.

An instance nobody maintains is a liability: the day it breaks is the day you find out the backup was never tested. This page sets up a nightly backup and restores it once, upgrades NetBox to a version you chose on purpose, wires the housekeeping job, and leaves a probe behind that tells you when something is wrong.

Before you start

Check
$systemctl is-active netbox netbox-rq postgresql
Expected output
active
active
active

Nightly backups

$sudo mkdir -p && sudo chmod 700
/usr/local/sbin/netbox-backup.sh
#!/usr/bin/env bash
set -euo pipefail
stamp=$(date +%Y%m%d-%H%M)
dest=
sudo -u postgres pg_dump -Fc netbox > "$dest/netbox-$stamp.dump"
files=(netbox/media netbox/netbox/configuration.py)
for f in netbox/netbox/ldap_config.py local_requirements.txt gunicorn.py; do
if [ -e "/$f" ]; then files+=("$f"); fi
done
tar -czf "$dest/netbox-files-$stamp.tar.gz" -C "${files[@]}"
find "$dest" -name 'netbox-*' -mtime + -delete
$sudo chmod 750 /usr/local/sbin/netbox-backup.sh
$sudo systemctl daemon-reload
$sudo systemctl enable --now netbox-backup.timer
$sudo systemctl start netbox-backup.service
Check
$systemctl is-active netbox-backup.timer
Expected output
active
Check
$ls  | sed 's/-[0-9]*-[0-9]*\././' | LC_ALL=C sort -u
Expected output
netbox-files.tar.gz
netbox.dump
If the service fails

journalctl -u netbox-backup -n 20 has the reason. The usual ones: pg_dump cannot connect (PostgreSQL is stopped, or pg_hba.conf lost its local all postgres peer line); tar complains about a missing netbox/media (the folder was moved, fix the path); no space left in .

Restore drill

$latest=$(ls -t /netbox-*.dump | head -1)
$sudo -u postgres createdb -O netbox netbox_drill
$sudo -u postgres pg_restore --no-owner --role=netbox -d netbox_drill < "$latest"
Check
$sudo -u postgres psql -d netbox_drill -tAc 'SELECT count(*) > 0 FROM dcim_site'
Expected output
t
Sensitive command — drops a database. Review before running.
$sudo -u postgres dropdb netbox_drill

Upgrade NetBox

$sudo systemctl start netbox-backup.service
$cd
$sudo git fetch --tags
$sudo git checkout
$sudo ./upgrade.sh
$sudo systemctl restart netbox netbox-rq
Check
$sudo git -C  describe --tags --exact-match
Expected output
Check
$curl -skf -H 'Authorization: Token ' https:///api/status/ | python3 -c 'import sys,json; print("v" + json.load(sys.stdin)["netbox-version"])'
Expected output
Check
$curl -sk -o /dev/null -w '%{http_code}' https:///login/
Expected output
200

Then open https:/// and click through a device page and a search: the API answering is not the same as the UI rendering, since static files and plugins can break one and not the other.

If a plugin blocks the upgrade

  • Check the plugin's compatibility table on its repository. If a compatible release exists, pin it in local_requirements.txt (netbox-bgp==0.15.0) and re-run sudo ./upgrade.sh.
  • If none exists, remove the plugin from PLUGINS in configuration.py and from local_requirements.txt, upgrade, and put it back when the plugin catches up. Its tables stay in the database; nothing is lost, the pages just disappear until then.
  • If the release notes say the plugin is now a core feature (it happens), migrate the data with the plugin's own instructions before upgrading.

Rollback

Sensitive command — drops a database. Review before running.
$sudo systemctl stop netbox netbox-rq
$cd
$sudo git checkout $(sudo git describe --tags --abbrev=0 ^)
$latest=$(ls -t /netbox-*.dump | head -1)
$sudo -u postgres dropdb netbox && sudo -u postgres createdb -O netbox netbox
$sudo -u postgres pg_restore --no-owner --role=netbox -d netbox < "$latest"
$sudo ./upgrade.sh
$sudo systemctl start netbox netbox-rq

Housekeeping

$sudo ln -sf /contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-housekeeping
$sudo /venv/bin/python /netbox/manage.py housekeeping
Check
$run-parts --test /etc/cron.daily | grep netbox
Expected output
/etc/cron.daily/netbox-housekeeping

Logs and disk: gunicorn writes to stdout, which systemd captures in the journal; cap it. nginx and Apache rotate their own files through logrotate, shipped with the package.

/etc/systemd/journald.conf.d/netbox.conf
[Journal]
SystemMaxUse=500M

Then sudo systemctl restart systemd-journald.

Weekly, five minutes:

CheckCommandExpected
Both services upsystemctl is-active netbox netbox-rqactive twice
Last backup ransystemctl list-timers netbox-backup.timera LAST within 24h
Backup size sanels -lh ${BACKUP_DIR}sizes in the same range as last week
Diskdf -h /under 80%
Queue quietredis-cli info memoryused_memory_human stable
New release?releases pageread the notes, plan the upgrade

Monitoring basics

Sensitive command — runs a remote script. Review before running.
$systemctl status netbox netbox-rq --no-pager | grep Active
$curl -skf -H "Authorization: Token " https:///api/status/ | python3 -m json.tool
Check
$curl -skf -H 'Authorization: Token ' https:///api/status/ | python3 -c 'import sys,json; print(json.load(sys.stdin)["rq-workers-running"] >= 1)'
Expected output
True

Done

Every night a dump and an archive land in , you have restored one by hand, NetBox runs , the change log is pruned daily, and a probe knows when the workers stop. The next page stops clicking: an automation user, pynetbox, bulk imports, webhooks and custom scripts.

Did everything work?

If you followed this page to the end on a real machine, say so. Your validation is dated and records your stack, so the next reader on the same path knows it still works.

This copy is read-only. To report that it works, or that it does not, open an issue

Only your stack choices are recorded, never your values. The pseudonym stays on this browser.