Deployment guide

LibreNMS Oxidized

Overview

Two tools do most of the day-to-day work of running a network. LibreNMS is an auto-discovering network monitoring system. Point it at a switch, router, firewall, server or PDU, and it finds the interfaces, sensors and services by itself, keeps history and draws the graphs. Oxidized collects device configurations and commits them to a git repository, so you can see exactly what changed and when, and roll back.

This guide covers deploying and configuring LibreNMS 26.7.0 and Oxidized 0.37.0 on Ubuntu 24.04 on Azure with the SecureAnyCloud Azure Marketplace image. The two products are already wired together in both directions.

LibreNMS is the single inventory. Oxidized takes its entire device list from LibreNMS's /api/v0/oxidized endpoint, authenticated with an API token generated for your VM. Add a device in LibreNMS and Oxidized starts backing up its configuration on the next cycle. There is no second list to maintain.

LibreNMS is the single interface. LibreNMS reads from the local Oxidized API, so each device's stored configuration and full version history appear on that device's Config tab in the LibreNMS web interface.

It works the moment it boots. Most monitoring products show nothing until you point them at your own equipment. This one monitors itself. On first boot, the VM configures its own SNMP agent, registers itself as a monitored device and runs a real discovery and poll. The first page you sign in to already shows a live host with real graphs, and Oxidized has already taken and committed a configuration backup of it.

Security by design: no baked login credential. Nothing usable ships in the image. On first boot, librenms-oxidized-firstboot.service generates the following secrets, all unique to your VM:

  • the MariaDB password
  • the Laravel application key
  • the LibreNMS admin password
  • the SNMPv2c community
  • the LibreNMS API token that Oxidized authenticates with
  • the ed25519 SSH keypair that Oxidized uses to log in

The secrets are written to /root/librenms-oxidized-credentials.txt (mode 0600, root only). The build-time admin password is overwritten with a random value that is generated, used once and discarded, so neither the image nor any person holds a working login for your machine.

Security by design: one front door. Oxidized's REST API has no authentication of its own, and an exposed SNMP community is worth protecting. For that reason, both listen on 127.0.0.1 only, and neither is reachable off the VM. LibreNMS's authenticated web interface is the single front door and serves the configuration views itself.

The account Oxidized uses to read this appliance's own configuration (oxbackup) is a separate, key-only, password-locked account. Its authorized_keys entry is restricted to loopback connections with no forwarding.

What you get:

  • LibreNMS 26.7.0 (pinned upstream release tag, GPL-3.0) with the database schema migrated
  • Oxidized 0.37.0 and oxidized-web 0.18.1 (Apache-2.0), with a git-backed configuration store
  • nginx + PHP 8.3-FPM + MariaDB 10.11 + rrdtool/rrdcached + Net-SNMP, configured and enabled
  • The two products integrated in both directions, verified on every build
  • Self-monitoring from first boot: a real device, a real poll, real graphs and a real configuration backup
  • Per-VM secret generation on first boot: six independent secrets, none of them shipped
  • Memory tuned for a 4 GiB VM with no swap (Azure certification requires it), and unattended security upgrades left enabled

Prerequisites

  • An active Azure subscription, an SSH public key, and a VNet with a subnet in the target region
  • A network security group that allows TCP 22 (SSH) and TCP 80 (the LibreNMS web interface) from your own address ranges only
  • For each device you want to monitor: SNMP enabled with a community or SNMPv3 credential, plus a login that Oxidized can use for configuration backup

The recommended size is Standard_B2ms (2 vCPU, 8 GiB). The image is built and tested to run within 4 GiB, so Standard_B2s works for evaluation and small estates. See Sizing for larger estates.

Step 1 - Deploy the VM

Deploy the image from the Azure Marketplace, or with the Azure CLI:

az vm image terms accept --urn sac1647283583153:librenms-oxidized-ubuntu-24-04:default:latest
az vm create \
  --resource-group my-network-ops-rg \
  --name librenms-oxidized \
  --image sac1647283583153:librenms-oxidized-ubuntu-24-04:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

First boot generates every secret, registers the VM as a monitored device and runs a discovery and poll. This takes around 70 seconds. Wait for the sentinel file before you sign in:

sudo test -f /var/lib/sac/data/librenms-oxidized-firstboot.done && echo "first boot complete"

Step 2 - Confirm the stack is running

Check that every service reports active:

for s in nginx mariadb php8.3-fpm rrdcached snmpd oxidized; do printf '%-16s %s\n' "$s" "$(systemctl is-active $s.service)" done

Confirm that the SNMP agent and the Oxidized API are bound to the loopback interface only. Port 80 should appear on 0.0.0.0, and ports 161 and 8888 on 127.0.0.1 only:

sudo ss -lntup | awk 'NR==1 || /:80 |:161 |:8888 /'

Step 3 - Retrieve your credentials

Every secret in the credentials file was generated on this VM and exists nowhere else. Print the file:

sudo cat /root/librenms-oxidized-credentials.txt

Keep this file safe and treat it as the only copy. The web interface is at http://<your-vm-ip>/.

Step 4 - Sign in to LibreNMS

Open http://<your-vm-ip>/ and sign in with the username and password from the credentials file.

Common default credentials such as admin/admin are not accepted, because no such account was ever created. To confirm that the credential from the file is the one that works, run:

CREDS=/root/librenms-oxidized-credentials.txt
U=$(sudo grep '^librenms.admin.user=' $CREDS | cut -d= -f2-)
J=$(mktemp)
TOK=$(curl -s -c "$J" http://127.0.0.1/login | grep -oE 'name="_token"[^>]*value="[^"]+"' | head -1 | sed 's/.*value="//;s/"//')
curl -s -o /dev/null -b "$J" -c "$J" -d "_token=$TOK" \
  --data-urlencode "username=$U" --data-urlencode "password=<LIBRENMS_ADMIN_PASSWORD>" \
  http://127.0.0.1/login
curl -s -o /dev/null -w 'signed in, landed on: %{url_effective}\n' -b "$J" -L http://127.0.0.1/ rm -f "$J"

A successful sign-in lands on http://127.0.0.1/. A rejected one lands back on http://127.0.0.1/login.

Step 5 - See the VM monitoring itself

Open Devices. The VM has already registered itself as localhost and polled itself.

Click through to the device. The overview shows real system detail read over SNMP, and real graphs drawn from RRD data collected by the poller.

From the shell, confirm that the poller is collecting data:

sudo mysql -N -B librenms -e \
  "SELECT hostname, os, version, last_polled FROM devices;"
sudo find /opt/librenms/rrd -name '*.rrd' | wc -l

LibreNMS ships its own health check, which catches misconfiguration that the web interface hides. You can run it at any time.

Note the cd /opt/librenms in these commands. sudo -u librenms keeps your home directory as the working directory, and the librenms account cannot enter it. Any LibreNMS command that shells out (fping, snmpwalk) then fails with a permission error. Changing directory first avoids this:

cd /opt/librenms && sudo -u librenms php validate.php

Two warnings are expected on this image: "Your install is over 24 hours out of date" and "Your local git branch is not master". The image deliberately ships a pinned, vetted release with LibreNMS self-update disabled, so daily.sh can never replace the tested tree. Security updates reach you as new SecureAnyCloud image versions instead.

Step 6 - See the Oxidized integration

Oxidized does not keep its own device list. It reads the list that LibreNMS serves, using the API token generated for this VM:

TOKEN=$(sudo grep '^librenms.api.token=' /root/librenms-oxidized-credentials.txt | cut -d= -f2-)
curl -s -H "X-Auth-Token: $TOKEN" http://127.0.0.1/api/v0/oxidized

The same endpoint rejects a request without a token, which keeps the inventory private:

curl -s -o /dev/null -w 'without a token: HTTP %{http_code}\n' http://127.0.0.1/api/v0/oxidized

Oxidized's own view shows what it did with that list, and the git store shows the versioned result:

curl -s http://127.0.0.1:8888/nodes.json
sudo -u oxidized git --git-dir=/var/lib/oxidized/configs.git log --oneline --stat | head

Now open the device in LibreNMS and select the Config tab. This is the other direction of the integration. LibreNMS reads the stored configuration back from Oxidized and renders it, with the sync status and the time of the last successful backup.

You can fetch the same content through the API that the tab uses:

TOKEN=$(sudo grep '^librenms.api.token=' /root/librenms-oxidized-credentials.txt | cut -d= -f2-)
curl -s -H "X-Auth-Token: $TOKEN" http://127.0.0.1/api/v0/oxidized/config/localhost | head -c 400

Step 7 - Add your first real device

Enable SNMP on the device you want to monitor, allowing your VM's address, then add the device. In the web interface, open Devices and choose Add Device. Or add it from the shell:

cd /opt/librenms
sudo -u librenms lnms device:add 192.0.2.10 --v2c --community 'your-community-string'
sudo -u librenms lnms device:discover 192.0.2.10
sudo -u librenms lnms device:poll 192.0.2.10

On any network you do not fully control, prefer SNMPv3:

cd /opt/librenms
sudo -u librenms lnms device:add 192.0.2.10 --v3 \
  --authlevel authPriv --authname monitor \
  --authpass 'auth-passphrase' --authalgo SHA \
  --cryptopass 'privacy-passphrase' --cryptoalgo AES

Configuration backup follows automatically. Oxidized reads the LibreNMS device list, so the device you just added appears in Oxidized on its next reload with no further configuration. It only needs a login that it can use.

Edit /var/lib/oxidized/.config/oxidized/config as the oxidized user, with the editor of your choice. The username, password and model_map keys hold the per-vendor logins and the mapping from LibreNMS OS to Oxidized model.

The image ships linux: linuxgeneric so that the appliance can back itself up. Add one line per platform you run, for example ios: ios or junos: junos. Then restart Oxidized:

sudo systemctl restart oxidized.service

Step 8 - Read a configuration diff

The value of configuration backup is the diff. Once a device has been backed up more than once, its version history appears on the same Config tab. From the shell, the git store gives you the full history:

sudo -u oxidized git --git-dir=/var/lib/oxidized/configs.git log --oneline | head
sudo -u oxidized git --git-dir=/var/lib/oxidized/configs.git show --stat HEAD

Step 9 - Put TLS in front of the interface

The image serves plain HTTP so that it works immediately on any address. For anything beyond evaluation, terminate TLS. Point a DNS name at the VM, then run:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>

Then set the URL that LibreNMS uses in generated links:

sudo -u librenms sed -i 's|^APP_URL=.*|APP_URL=https://<your-domain>|' /opt/librenms/.env cd /opt/librenms && sudo -u librenms php artisan config:clear

Sizing for larger estates

The image is tuned to run the whole stack inside 4 GiB, deliberately and without swap, because Azure certification rejects swap baked into an image. These are the settings that matter as you grow.

Poller workers. /etc/cron.d/librenms runs poller-wrapper.py with 2 workers. Upstream's default is 16, which cannot coexist with MariaDB, PHP-FPM and Oxidized inside 4 GiB. Raise the worker count as your device count grows, at roughly one worker per 20 to 30 devices, and raise the VM size with it.

Database buffer pool. /etc/mysql/mariadb.conf.d/60-sac-librenms.cnf sets innodb_buffer_pool_size to 192M and turns performance_schema off. On a VM with 8 GiB of RAM or more, raising the buffer pool to around a quarter of RAM is the single most effective change.

PHP-FPM workers. /etc/php/8.3/fpm/pool.d/librenms.conf allows 6 children, which is ample for a handful of operators.

Check the headroom under real load before you change anything:

free -m ps -eo rss,comm --sort=-rss | head -8

Security recommendations

  • Restrict the network security group so that TCP 80 and TCP 22 are reachable only from your own ranges, and put TLS in front of the interface (Step 9).
  • Leave the SNMP agent and the Oxidized API on the loopback interface. The Oxidized API has no authentication of its own, so exposing port 8888 would publish every stored device configuration to anyone who can reach it.
  • Treat /root/librenms-oxidized-credentials.txt as the only copy of your secrets. Treat the Oxidized configuration file as sensitive too, because it holds the logins that Oxidized uses to reach your devices.
  • On any network you do not fully control, prefer SNMPv3 with authentication and privacy over SNMPv2c communities.
  • Give Oxidized a read-only account on each device wherever the platform supports one.
  • Leave unattended security upgrades enabled.

Troubleshooting

A device shows as down immediately after it is added. LibreNMS needs a successful SNMP response. Test reachability from the VM first:

cd /opt/librenms && sudo -u librenms lnms device:poll localhost

Graphs are empty on a newly added device. Graphs need at least two poll cycles, so allow ten minutes. The poller runs every five minutes from /etc/cron.d/librenms.

Oxidized shows no nodes. Oxidized reloads the LibreNMS device list on its own schedule. Restart it to reload immediately, then check its log:

sudo systemctl restart oxidized.service
sudo journalctl -u oxidized.service -n 40 --no-pager

A device backs up in Oxidized but the Config tab is empty. The tab reads through the LibreNMS API. Confirm that the integration settings survived any manual edits:

cd /opt/librenms
sudo -u librenms lnms config:get oxidized.enabled
sudo -u librenms lnms config:get oxidized.url

snmpwalk on the VM says "Unknown Object Identifier". Ubuntu ships /etc/snmp/snmp.conf with MIB loading disabled, so the command-line tools need numeric OIDs. This does not affect LibreNMS, which passes its own bundled MIB directory. For example, query sysName.0 like this:

COMM=$(sudo grep '^snmp.community=' /root/librenms-oxidized-credentials.txt | cut -d= -f2-) snmpget -v2c -c "$COMM" -Oqv 127.0.0.1 .1.3.6.1.2.1.1.5.0

Licensing and support

LibreNMS is free, open-source software published under the GNU General Public License version 3.0. Oxidized is published under the Apache License 2.0. There is no licence fee and no key to enter. SecureAnyCloud is not affiliated with either project.

The SecureAnyCloud charge covers packaging, security patching, image maintenance and 24/7 expert support. Contact sales@secureanycloud.com.

Deploy on Azure

Launch LibreNMS 26.7.0 and Oxidized 0.37.0 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure