Integrating NetLens with LibreNMS
Share the NetLens (NfSen) data folders over NFS so a LibreNMS server on a different machine can show NetFlow graphs and Top-N statistics.
1. Overview
LibreNMS has a built-in Netflow tab. It needs three things from NfSen:
- RRD graph files — read via config
nfsen_rrds(profiles-stat/live/<source>.rrd) — the channel graphs. - Raw flow files — read via the
nfdumpbinary, which LibreNMS runs on its own server:nfdump -M <base>/profiles-data/live/<source> -T -R <range> -n N -s .... It must be able to seeprofiles-data/live/...and havenfdumpinstalled. This produces the Top-N statistics. - Matching names — the NfSen source (ident) must map to the LibreNMS device hostname (section 6).
When LibreNMS runs on a different machine (your case), the cleanest way to give it items 1 and 2 is an NFS read-only share of the two data folders.
- NfSen (Docker) stays on the VPS and keeps writing to its local folders — we never let another writer touch the live data.
- Share only two folders over NFS, read-only:
nfsen-data/(raw flow files) andnfsen-stat/(RRD graphs). - Install
nfdump 1.6.25on the LibreNMS server to match the container's v1.6 file format (see section 5 for why not 1.7.3 or 1.6.17). - Tell LibreNMS where the data is (
lnms config:set) — the Netflow tab appears on every device page. - Never run two NfSen instances writing to the same shared folder. Ever.
2. Architecture
Your VPS (Docker NetLens) LibreNMS server
--------------------------- -----------------
docker-compose.yml LibreNMS web UI
|-- nfsen-data/ = profiles-data --NFS(ro)--> /var/nfsen/profiles-data
|-- nfsen-stat/ = profiles-stat --NFS(ro)--> /var/nfsen/profiles-stat
|-- nfsen-var/ = logs (NOT shared)
`-- nfsen-etc/ = config (NOT shared - contains .htpasswd!)
NfSen writes here | LibreNMS READS here (never writes)
(one writer only) | nfdump (1.6.25) reads the flow files
v RRD files render the channel graphs
3. NFS server setup (on the VPS running NetLens)
Step 1 — Install the NFS server
sudo apt update
sudo apt install -y nfs-kernel-server
Step 2 — Find your project path
cd /path/to/netlens # where nfsen-data/ and nfsen-stat/ live (e.g. /root/netlens)
pwd # remember this as <VPS_PROJECT_PATH>
Step 3 — Add the exports
Edit /etc/exports and add (replace the IP and path):
<VPS_PROJECT_PATH>/nfsen-data <LibreNMS_IP>/32(ro,sync,no_subtree_check)
<VPS_PROJECT_PATH>/nfsen-stat <LibreNMS_IP>/32(ro,sync,no_subtree_check)
Example (LibreNMS server = 192.168.1.50, project in /root/netlens):
/root/netlens/nfsen-data 192.168.1.50/32(ro,sync,no_subtree_check)
/root/netlens/nfsen-stat 192.168.1.50/32(ro,sync,no_subtree_check)
ro= read-only (recommended; the LibreNMS server only needs to read).- We deliberately do not export
nfsen-var/ornfsen-etc/—nfsen-etccontains.htpasswd(your Web UI password hashes). - If you ever want NfSen to write onto the share instead (alternative pattern, section 11), change to
rw + no_root_squashthen. Not now.
Step 4 — Activate and verify
sudo exportfs -rav
sudo systemctl enable --now nfs-server
sudo showmount -e localhost # you should see the 2 exports
Step 5 — Firewall (cloud VPS + ufw)
Allow NFS from the LibreNMS IP:
sudo ufw allow from 103.159.37.199 to any port 111 proto tcp
sudo ufw allow from 103.159.37.199 to any port 2049 proto tcp
sudo ufw allow from 103.159.37.199 to any port 111 proto udp
sudo ufw allow from 103.159.37.199 to any port 2049 proto udp
20048+ etc. If mounting fails, temporarily disable ufw to test, then open the ports the error mentions.4. NFS client setup (on the LibreNMS server)
Step 1 — Install the NFS client
sudo apt update
sudo apt install -y nfs-common
Step 2 — Create the mount points
Important: use the same paths LibreNMS expects, so no symlinks are needed:
sudo mkdir -p /var/nfsen/profiles-data /var/nfsen/profiles-stat
Step 3 — Mount (replace <VPS_IP> and the project path)
sudo mount -t nfs4 <VPS_IP>:<VPS_PROJECT_PATH>/nfsen-data /var/nfsen/profiles-data
sudo mount -t nfs4 <VPS_IP>:<VPS_PROJECT_PATH>/nfsen-stat /var/nfsen/profiles-stat
Example:
sudo mount -t nfs4 103.187.23.163:/root/netlens/nfsen-data /var/nfsen/profiles-data
sudo mount -t nfs4 103.187.23.163:/root/netlens/nfsen-stat /var/nfsen/profiles-stat
Step 4 — Make it permanent (/etc/fstab)
Append these two lines (each has exactly 6 fields: device, mountpoint, fstype, options, dump, pass):
<VPS_IP>:<VPS_PROJECT_PATH>/nfsen-data /var/nfsen/profiles-data nfs4 ro,soft,timeo=50,retrans=2,_netdev 0 0
<VPS_IP>:<VPS_PROJECT_PATH>/nfsen-stat /var/nfsen/profiles-stat nfs4 ro,soft,timeo=50,retrans=2,_netdev 0 0
(ro + soft = safe for a monitoring box: if NFS hiccups, LibreNMS just shows "no data" instead of hanging forever. _netdev tells systemd to wait for the network before mounting at boot.)
- Write only the two fstab lines above. Do not paste the
sudo mountcommands from Step 3 into/etc/fstab— boot then fails with"/etc/fstab: parse error"and the shares never come back. - Check the file is clean:
sudo cat -A /etc/fstab(each line must end with just$— no^M/CRLF if you pasted from Windows). - After a reboot the mounts must be there with plain
df -h | grep nfsen. If they are gone, runsudo mount -a(should be silent) and re-check fstab.
Step 5 — Verify you can see the data
ls /var/nfsen/profiles-data/live/ # should list your sources, e.g. router1
ls /var/nfsen/profiles-stat/live/ # should list .rrd files
5. Install nfdump on the LibreNMS server (important version rule)
LibreNMS runs nfdump against your flow files. The file format must match the container's nfdump, otherwise LibreNMS cannot read anything:
- Your Docker container uses nfdump 1.6.17 (raw flow file format v1.6.x).
- nfdump 1.7+ writes/reads a NEW binary format — it cannot read 1.6.17 files.
- Your LibreNMS is Ubuntu 24.04: the default package there is nfdump 1.7.3 = WRONG. Do not use it.
- Ubuntu 20.04's apt package is nfdump 1.6.17 = perfect match (only relevant if you ever run LibreNMS on 20.04).
nfdump -M ... -R . dies with "*** buffer overflow detected ***". 1.6.25 is the last release of the 1.6.x tree — same v1.6 file format as the container's 1.6.17, but with years of bug fixes. Build it from source:Step 1 — Install the build dependencies
sudo apt update
sudo apt install -y build-essential autoconf automake libtool pkg-config \
flex bison byacc libpcap-dev libbz2-dev
Step 2 — Download and extract the 1.6.25 source
cd /tmp
wget https://github.com/phaag/nfdump/archive/v1.6.25.tar.gz
tar xzf v1.6.25.tar.gz
cd nfdump-1.6.25
Step 3 — Build and install (same configure family the Dockerfile uses)
sh ./autogen.sh
./configure --prefix=/usr/local
make -j"$(nproc)"
sudo make install
sudo ldconfig
Step 4 — Verify the version
/usr/local/bin/nfdump -V # must show: Version: 1.6.25
(Ignore any /usr/bin/nfdump 1.7.3 that apt left behind — LibreNMS will be pointed at /usr/local/bin/nfdump, section 6.)
Step 5 — Quick read test (should print flow stats, not an error)
/usr/local/bin/nfdump -M /var/nfsen/profiles-data/live/router1 -R . -s record/flows | tail -5
- The
-Margument is the full path to the source directory (<base>/profiles-data/live/<source>). -Rselects the time range..= all files. Without-R, nfdump refuses with:"-M needs either -r or -R to specify the file or file list."-R .reads every file since the beginning — on a busy profile that takes minutes (18M flows took ~40 s in testing). It is not stuck. For a fast check use a file range instead:/usr/local/bin/nfdump -M /var/nfsen/profiles-data/live/<source> -R 'nfcapd.202608151945:nfcapd.202608151950' -s record/flows | tail -5- Never pass a directory path to
-R— nfdump 1.6.x tries to parse it as a time range and crashes with"*** buffer overflow detected ***". - If it says
Can't open ... permission denied, fix per section 9.
6. Configure LibreNMS
Step 1 — Run these as the librenms user (or with sudo -u librenms)
lnms config:set nfsen_enable true
lnms config:set nfsen_split_char '_'
lnms config:set nfsen_base.+ '/var/nfsen/'
lnms config:set nfsen_rrds.+ '/var/nfsen/profiles-stat/live/'
lnms config:set nfsen_rrds.+ '/var/nfsen/profiles-stat'
lnms config:set nfdump /usr/local/bin/nfdump # the 1.6.25 we built (NOT /usr/bin/nfdump = 1.7.3)
lnms config:set nfsen_subdirlayout 0 # IMPORTANT: this NfSen stores flow files FLAT
# (nfcapd.YYYYMMDDHHMM, no subdirs - its nfsen.conf
# has no $SUBdirlayout). Without 0, LibreNMS looks
# for files in YYYY/MM/DD/ subdirs and finds nothing.
lnms config:set nfsen_suffix '_none' # REQUIRED, never leave empty: LibreNMS runs
# preg_replace('/' . nfsen_suffix . '/', ...) and an
# empty suffix = empty regex = NULL = the RRD check
# always fails = Netflow tab never appears. Use any
# string that never occurs in device hostnames.
_none (the LibreNMS docs' trick): lnms config:set nfsen_suffix '_yourdomain_com'. Whatever you choose, the value must be non-empty — empty = no Netflow tab.Step 2 — Find the tab
Open LibreNMS → Devices → click the device → Netflow tab at the end of the tab bar under the device header (scroll it sideways if it wraps). Quick test: open http://<librenms>/device/device=<id>/tab=netflow/.
Step 3 — Tab missing?
Work through section 8. The three usual causes: nfsen_enable not true, nfsen_suffix empty, or the RRD file name not matching the device's actual hostname (section 7).
Step 4 — What you should see
Stats (Top N) use nfdump → your NFS data. Graphs use the RRDs. Note: the General view can look empty (it looks for per-channel subfolders this NfSen doesn't create) — the Stats view is the one with your flow data.
Tuning knobs (optional, all documented in LibreNMS):
lnms config:set nfsen_last_default 900
lnms config:set nfsen_top_default 20
lnms config:set nfsen_stats_default srcip
lnms config:set nfsen_order_default packets
lnms config:set nfsen_last_max 153600 # max seconds for stats
7. Matching NfSen sources to LibreNMS devices (the common gotcha)
LibreNMS finds a device's flow data by name. The NfSen source (ident) name becomes the RRD filename and the directory name under profiles-data/live/.
- The NfSen ident must match the LibreNMS device hostname, but: NfSen idents are limited to 21 characters, and dots (
.) are replaced bynfsen_split_char(we set_). - Example: LibreNMS device hostname
core-router.example.com(longer than 21) → NfSen source identcore_router_example_com(dots →_, short). Withnfsen_split_char '_'set, LibreNMS looks for the RRD named after the transformed hostname.
Devices added by IP — use symlinks
If your device is added by IP in LibreNMS: create symbolic links so the IP-based names resolve to the real source. You need two — one for the RRD graphs and one for the raw flow data (nfdump stats use it too).
ro), so you cannot create symlinks on the LibreNMS server — the mount rejects writes. Create them on the VPS instead (inside the project folders, next to docker-compose.yml — they show up on the LibreNMS side through NFS):# on the VPS (NfSen host), inside the project folder:
cd /path/to/netlens/nfsen-stat/live
sudo ln -s router1.rrd 192_168_1_50.rrd # <deviceIP with _ >.rrd
cd /path/to/netlens/nfsen-data/live
sudo ln -s router1 192_168_1_50 # data dir for nfdump stats
(Replace router1 with the real source that collects that router's traffic, and 192_168_1_50 with the LibreNMS device hostname/IP with . replaced by nfsen_split_char, i.e. _.)
"device:poll 192.168.1.50"). A symlink with the wrong name means the Netflow tab never appears — the name must match exactly, e.g. a device polled as 103.187.22.1 needs 103_187_22_1.rrd.On the NfSen side, when you add a real source in nfsen.conf, name it to match (see the project README for adding sources):
'core_router_example_com' => { 'port' => '2070', 'col' => '#FF0000', 'type' => 'netflow' },
8. Verification checklist
On the VPS (NetLens/NfSen):
sudo showmount -e localhost
ls -la nfsen-data/live/ nfsen-stat/live/
On the LibreNMS server:
df -h | grep nfsen # mounts are up
ls /var/nfsen/profiles-data/live/ # sources + any IP symlinks
ls /var/nfsen/profiles-stat/live/ # .rrd files + any IP symlinks
/usr/local/bin/nfdump -V # must be 1.6.25
/usr/local/bin/nfdump -M /var/nfsen/profiles-data/live/<source> -R 'nfcapd.<file1>:nfcapd.<file2>' -s record/flows | tail -5
lnms config:get nfsen_enable # must be true
lnms config:get nfsen_suffix # must NOT be empty
lnms config:get nfsen_subdirlayout # must be 0 (flat file layout)
lnms config:get nfsen_rrds # must list the profiles-stat paths
sudo -u librenms ./validate.php # LibreNMS self-check (in its install dir)
In the browser: Device page → tab bar (at the end of it) → Netflow → Stats view shows Top-N. Direct URL test: http://<librenms>/device/device=<id>/tab=netflow/
sudo tail -n 50 /opt/librenms/logs/librenms.log and check, in order: nfsen_enable true? nfsen_suffix non-empty? RRD file name matches the device's real hostname (section 7)? The tab only appears when all three are true.9. Permissions & ownership (the uid story)
Inside the container the users are:
netflow= uid 1000 (ownsprofiles-data/live— writes flow files)www-data= uid 33 (ownsprofiles-stat— writes RRDs)
Over NFS, uid 33 and uid 1000 are just numbers. Your live directories are chmod 775 with netflow:www-data ownership, so any local user (including www-data/root, which is how LibreNMS runs nfdump) can read them. That is why a read-only export works with no special uid mapping.
If you ever see "Permission denied":
- Check perms on the VPS:
ls -la nfsen-data/live/(expectdrwxrwxr-x) - Check the export flags:
cat /etc/exports(rois fine for reading) - On some setups the export needs:
rw,sync,no_root_squash(only needed if NfSen itself must write to the share — section 11)
10. Danger zone — things that WILL break your data
nfsen-etc/ — it contains .htpasswd (Web UI password hashes).nfsen-var/ — unnecessary (logs, runtime)./usr/local/bin/nfdump.nfsen_suffix EMPTY in LibreNMS — the tab's RRD check builds an empty regex and silently fails, so the Netflow tab never appears.sudo mount commands into /etc/fstab instead of the proper 6-field lines — boot fails to mount the shares ("parse error").11. Alternative pattern (only if you change your mind later)
Pattern B — NFS as the primary storage for the NfSen data (single writer):
- Mount the NFS share on the VPS under the project (e.g. replace the
nfsen-data/folder with an NFS mount to a NAS). - Docker keeps working unchanged (it's still a bind mount).
- Requirements: export
rw,sync,no_root_squash(the entrypoint runs chown/chmod as root), the NFS server must be reliable, and the mount must be up beforedocker compose up. - Pros: data survives VPS loss; easy to point multiple read-only consumers (LibreNMS) at the same NAS.
- Cons: single point of failure (the NAS) + NFS dependency at boot.
12. Summary
- Goal: LibreNMS (separate server) shows NetFlow graphs + top-N stats from the Dockerized NfSen.
- How: NFS share the two data folders (
nfsen-data= raw flows,nfsen-stat= RRD graphs) read-only from the NetLens VPS to the LibreNMS server. NfSen keeps writing locally (one writer rule — never two NfSen instances on the same live data). - On the LibreNMS server: install
nfs-common, mount the shares at/var/nfsen/profiles-dataand/var/nfsen/profiles-stat, and install nfdump 1.6.25 (last of the 1.6.x tree) to match the container's v1.6 file format — on Ubuntu 24.04 build it from source, do not use 1.6.17 (its range parsing crashes with "buffer overflow detected" on 24.04's toolchain). Then enable the integration withlnms config:set nfsen_enable true(+ paths + nfdump →/usr/local/bin/nfdump). - NfSen source names must match LibreNMS device hostnames (21-char limit, dots →
_); for devices added by IP create two symlinks on the VPS (RRD.rrd+ data dir) named after the device's real hostname (check the poll log, e.g.103.187.22.1→103_187_22_1.rrd). The mount is read-only, so the symlinks must be created VPS-side. - LibreNMS config that must be right:
nfsen_enabletrue,nfsen_suffixnon-empty (empty = no tab),nfsen_subdirlayout0 (this NfSen stores flat nfcapd files),nfsen_rrdspointing atprofiles-stat. - fstab entries need
_netdevand must be proper 6-field lines — pasting thesudo mountcommands into fstab breaks boot mounting. - Never export
nfsen-etc(contains.htpasswd) ornfsen-var.
nfdump -M <base>/profiles-data/live/<source>).