LibreNMS and Oxidized Network Ops Stack on Ubuntu 24.04 on Azure User Guide
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/oxidizedendpoint, 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_B2sworks 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 /'

Step 3 — Retrieve your credentials
Every secret below was generated on this VM and exists nowhere else:
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. 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.

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 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

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

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 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/librenmsrunspoller-wrapper.pywith 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.cnfsetsinnodb_buffer_pool_sizeto 192M and turnsperformance_schemaoff. 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.confallows 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.txtas 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.