Networking Azure

LibreNMS and Oxidized Network Ops Stack on Ubuntu 24.04 on Azure User Guide

| Product: LibreNMS 26.7.0 and Oxidized 0.37.0 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of LibreNMS 26.7.0 and Oxidized 0.37.0 on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images.

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, commits them to a git repository and versions them, so you can see exactly what changed, when, and roll back.

They are already wired together, in both directions. This is the point of the image:

  • 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 is configured to read from the local Oxidized API, so each device's stored configuration and its full version history render on that device's Config tab inside the LibreNMS web interface.

It works the moment it boots. Most monitoring products cannot show you anything 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, so 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, all unique to your VM: the MariaDB password, the Laravel application key, the LibreNMS admin password, the SNMPv2c community, the LibreNMS API token Oxidized authenticates with, and the ed25519 SSH keypair Oxidized uses to log in. They 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 no image and no 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 having, so 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 whose authorized_keys entry is restricted to loopback connections with no forwarding.

What is included:

  • 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

  • Active Azure subscription, an SSH public key, and a VNet plus subnet in the target region

  • A network security group allowing TCP 22 (SSH) and TCP 80 (the LibreNMS web interface) from your own address ranges only

  • Recommended size 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

  • For each device you want to monitor: SNMP enabled with a community or SNMPv3 credential, and for configuration backup, a login Oxidized can use

Step 1 — Deploy the VM

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

az vm create \
  --resource-group my-network-ops-rg \
  --name librenms-oxidized \
  --image cloudimg: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, which takes around 70 seconds. Wait for the sentinel before signing in:

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

Step 2 — Confirm the stack is running

Every service should report 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 really are bound to the loopback interface only. You should see port 80 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 /'

systemctl reports nginx, mariadb, php8.3-fpm, rrdcached, snmpd and oxidized all active, and the listening socket table shows the web interface on port 80 while the SNMP agent on 161 and the Oxidized API on 8888 are bound to 127.0.0.1 only

Step 3 — Retrieve your credentials

Every secret below was generated on this VM and exists nowhere else:

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

the credentials file listing the LibreNMS URL, admin username, admin password, database name, user and password, the API token, the SNMP community, the Oxidized REST address, and the oxbackup device login and key path, with the secret values masked

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.

the LibreNMS sign in page served over the VM public address, asking for a username and password

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

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.

the LibreNMS devices list showing the self monitored localhost device with its operating system, uptime and status, discovered automatically on first boot

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.

the LibreNMS device overview for localhost showing the detected Ubuntu 24.04 operating system, kernel, uptime and hardware, alongside a memory graph that has drawn real collected data and physical, virtual, buffer, cached and shared memory readings

From the shell you can confirm the poller is genuinely collecting:

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 the web interface hides. Run it 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, which makes any LibreNMS command that shells out (fping, snmpwalk) fail with a permission error. Changing directory first avoids it:

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

the LibreNMS validate.php health check reporting the component versions and a list of OK results covering the database connection, schema, poller, locks, rrdtool and the rrdcached connection

Two warnings are expected and correct 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 cloudimg image versions instead.

Step 6 — See the Oxidized integration

Oxidized does not keep its own device list. It reads the one 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 without a token is rejected, which is what 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

the LibreNMS API returning the oxidized device list containing localhost, the Oxidized node status reporting model LinuxGeneric with status success, and the git log of the configuration store showing a commit for localhost with 196 insertions

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.

the Config tab of the localhost device inside LibreNMS showing sync status success, the node name, IP, LinuxGeneric model and last sync time, above the retrieved device configuration listing the hostname, hosts file and interface detail

You can fetch the same content through the API that 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 it. Do this in the web interface under Devices → Add Device, or 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

For SNMPv3, which is preferable on any network you do not fully control:

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. Because Oxidized reads the LibreNMS device list, the device you just added appears in Oxidized on its next reload with no further configuration. What it needs from you is a login 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 are where per vendor logins and the LibreNMS-OS to Oxidized-model mapping live. The image ships linux: linuxgeneric so that the appliance can back itself up; add a line per platform you run, for example ios: ios or junos: junos. Then restart it:

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, the version history is on the same Config tab, and 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 it works immediately on any address. For anything beyond evaluation, terminate TLS. With a DNS name pointed at the VM:

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

Then set the URL 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. 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 it as your device count grows, roughly one worker per 20 to 30 devices, and raise the VM size with it.

  • Database buffer pool. /etc/mysql/mariadb.conf.d/60-cloudimg-librenms.cnf sets innodb_buffer_pool_size to 192M and turns performance_schema off. On an 8 GiB or larger VM, 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 headroom under real load before changing anything:

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

Security recommendations

  • Restrict the network security group so 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, and the Oxidized configuration file as sensitive, since it holds the logins Oxidized uses to reach your devices

  • Prefer SNMPv3 with authentication and privacy over SNMPv2c communities on any network you do not fully control

  • 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 being 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. It 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 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, sysName.0 is:

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 and 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. cloudimg is not affiliated with either project.

The cloudimg charge covers packaging, security patching, image maintenance and 24/7 expert support. Contact support@cloudimg.co.uk.