
How Nlbwmon Maps OpenWrt Traffic to Devices
Use luci-app-nlbwmon for per-device bandwidth monitoring on OpenWrt. It shows which local host moved traffic, with rows tied to IP and MAC addresses where available. That makes it the right first tool for finding the laptop, camera, or guest device eating the connection. The package command depends on your OpenWrt branch, though. Use apk on the 25.12 branch and compatible main snapshots. Use opkg on 24.10 and earlier.
Last updated: 2026-08-03
Is luci-app-nlbwmon the right per-device bandwidth monitor on OpenWrt?
Yes, if your question is which device used the bandwidth. luci-app-nlbwmon adds the LuCI web interface, while nlbwmon does the accounting underneath.
I use it for home networks and small offices because it answers three useful questions:
- Which IP or MAC address moved the traffic?
- Was that traffic uploaded or downloaded?
- Which accounting period did it happen in?
It is not a speed test, a packet capture, or an ISP billing meter. It also does not enforce limits. If you install it expecting those jobs, the graphs will disappoint you for perfectly valid reasons.
The useful split is simple. nlbwmon provides per-device accountability. An interface monitor such as vnStat provides whole-link history. Keep both if you need both views.
What does nlbwmon actually count?

nlbwmon, short for Netlink Bandwidth Monitor, reads accounting data from the Linux connection tracking system. Connection tracking, usually called conntrack, maintains state and counters for network flows passing through the router.
Here is what happens. A LAN device opens a connection, the firewall tracks that flow, and nlbwmon assigns the byte and packet counters to the local host. It then rolls those counters into accounting periods and stores them in its database.
That model gives you per-host data without sniffing every packet. However, it also creates limits you need to understand.
Router-originated traffic may not appear under a LAN device. Traffic on an unlisted local network may also be missing from the view you expected. Hardware flow offloading can cause another gap because some packets stop following the normal software accounting path.
So treat nlbwmon as a host attribution tool. Do not treat it as proof that every byte seen by the WAN interface belongs in one of its device rows.
Which install command applies to your OpenWrt release?
Run this first:
. /etc/openwrt_release
printf 'Release: %s\n' "$DISTRIB_RELEASE"
command -v apk
command -v opkg
The release line tells you which firmware branch is installed. The two command checks tell you which package manager actually exists on that router. Trust that output before copying an install command.
OpenWrt moved main snapshots to apk after the package-manager transition announced in November 2024. The 25.12 branch also uses apk. OpenWrt 24.10 and earlier still use opkg. The official OpenWrt package-manager announcement explains the change.
For the 25.12 branch and compatible main snapshots, run:
apk update
apk add luci-app-nlbwmon
apk update refreshes the repository indexes. The second command installs the LuCI application and pulls in the required daemon package.
For OpenWrt 24.10 and earlier, run:
opkg update
opkg install luci-app-nlbwmon
Again, the first command refreshes package metadata. The second installs the web interface and its dependencies.
Do not install an old .ipk on an apk system because a forum post still ranks well. Do not mix package feeds from another firmware build either. If the package manager reports an architecture or dependency error, verify that every configured feed matches the installed firmware.
After installation, start the service and read its status:
/etc/init.d/nlbwmon enable
/etc/init.d/nlbwmon restart
/etc/init.d/nlbwmon status
logread -e nlbwmon
The status command confirms whether the daemon stayed running. logread shows startup and database errors instead of making you guess from an empty LuCI page.
The menu normally appears under the LuCI status pages. Its exact label can vary between builds. If it is missing, sign out of LuCI and reload before deleting caches or reinstalling packages.
How should you configure nlbwmon before trusting it?
Read /etc/config/nlbwmon before you read the graphs. The defaults may be usable, but they cannot know which subnets you consider local or where you want history stored.
Start with:
uci show nlbwmon
That prints the active Unified Configuration Interface, or UCI, settings. Check the configured local networks, database directory, accounting period, refresh behavior, and commit settings.
Next, find the database path and identify the filesystem behind it:
dbdir="$(uci -q get 'nlbwmon.@nlbwmon[0].database_directory')"
printf 'Database directory: %s\n' "$dbdir"
df -h "$dbdir"
That output tells you whether the database sits on persistent storage or a memory-backed filesystem. A path under /var is not automatically persistent on OpenWrt. On many builds, /var lives in RAM because Linux apparently needed one more way to make a familiar path mean something different.
If the database is volatile, history disappears after a reboot. Move it to suitable persistent storage if you need long-term records. However, do not shorten the commit interval without thinking. More frequent writes reduce the amount lost during a crash, but they also write to flash more often.
I also check the local-network entries. If a guest network, virtual LAN, or routed subnet is missing, its traffic may not be attributed as expected. When you separate devices with VLANs, keep the nlbwmon network list aligned with that design. A clean VLAN setup for a home lab makes the accounting easier to read.
After any UCI change, restart the daemon and check the log:
/etc/init.d/nlbwmon restart
logread -e nlbwmon
A successful restart proves that the daemon accepted the configuration. An empty graph does not.
How do you monitor OpenWrt traffic per IP address?
Open the nlbwmon page in LuCI and select the current accounting period. Each row represents a local host, normally with an IP address and a MAC address where the router can determine one.
For cleaner records, assign static DHCP leases to devices you care about. Stable addresses keep a camera or server from appearing under several IPs after lease changes. Device names also make the table useful to someone who does not keep MAC addresses in their head.
However, static leases do not solve every identity problem. Phones and laptops may use randomized MAC addresses. IPv6 privacy addressing can also produce several addresses for one physical device. In those cases, expect split rows unless the monitor can still associate the traffic with the same local host.
Use the command-line client when the LuCI table looks suspicious:
nlbw -c show
nlbw -c csv
The formatted view is useful at the terminal. The comma-separated value output is better for sorting, scripts, and checking the stored counters without LuCI formatting.
Check the period before comparing results. A new accounting period can make the current total look unexpectedly low while older data remains in the database. That is rollover, not missing traffic.
Why do the LuCI bandwidth totals look wrong?
Check units, periods, and accounting boundaries before blaming LuCI. nlbwmon stores counter values, while the frontend formats them into readable units. Rounding can hide small totals, and different suffixes make two rows easy to misread.
The other common mistake is comparing bytes with a speed result expressed in bits per second. Those are different units and different kinds of measurement. One is accumulated traffic. The other is a transfer rate.
Then check direction. Download and upload are shown from the local host’s point of view, but other tools may label receive and transmit from the interface’s point of view. A WAN interface receives traffic that a LAN client considers a download. Both labels can be correct.
Use the raw output to settle the unit question:
nlbw -c csv > /tmp/nlbwmon.csv
head /tmp/nlbwmon.csv
The first command writes the stored rows without the LuCI display layer. The second shows the column names and initial records, which tells you what you are actually adding up.
If the raw rows match LuCI after formatting, there is no accounting bug. If they do not, save the CSV, note the selected period, and check logread. Now you have a reproducible problem instead of a screenshot with no time anchor.
Why does nlbwmon disagree with vnStat or the WAN counter?
The tools count at different boundaries. nlbwmon attributes tracked flows to local hosts. vnStat reads interface counters and records totals for an interface such as the WAN device or bridge.
Those totals can differ because the WAN interface also sees traffic that does not belong to a normal LAN client row. Router updates, tunnel overhead, firewall traffic, and retransmissions can all affect the comparison. Bridged, offloaded, or separately routed traffic adds more ways for the numbers to part company.
Do not subtract the two totals and call the difference packet loss. That conclusion does not follow from the counters.
Instead, compare matching periods and decide which question you are answering:
- Use
nlbwmonto find the local device responsible for traffic. - Use vnStat to follow traffic trends on one interface.
- Use firewall or interface counters to diagnose forwarding paths.
- Use packet capture when you need evidence about individual packets.
None of these should be treated as an exact copy of an ISP’s billing system. The observation points are different.
Which OpenWrt bandwidth monitor should you choose?
Start with luci-app-nlbwmon for per-device records. Add another tool only when you can name the missing view.
| Tool | What it answers | What it counts | Where it falls short |
|---|---|---|---|
nlbwmon with luci-app-nlbwmon | Which local device used traffic? | Conntrack flow counters attributed to hosts | Can miss or undercount traffic outside its accounting path |
vnStat with luci-app-vnstat or luci-app-vnstat2 | How much traffic crossed an interface over time? | Interface counters | No useful per-device breakdown |
wrtbwmon with luci-app-wrtbwmon | Which device is active right now? | Live per-host firewall counters | History and compatibility depend on the build |
luci-app-trafficctl | Can this package control or account for traffic on this firmware? | Depends on the package source and implementation | Not present in every official feed and not a standard substitute for nlbwmon |
| Packet capture or eBPF | Which packets, processes, or protocol events caused the traffic? | Packet or kernel event data | More setup, storage, and analysis |
For vnstat on OpenWrt, search the package feed before assuming the LuCI package name. Some branches carry a different frontend package than older guides mention.
With apk, run:
apk search '*vnstat*'
With opkg, run:
opkg list | grep -i vnstat
The output tells you which vnStat daemon and LuCI frontend packages exist for that exact firmware. Install what the feed provides, not a package name copied from another release.
I treat luci-app-wrtbwmon as a live troubleshooting view, not the main accounting database. Older wrtbwmon setups may depend on firewall behavior that changed as OpenWrt moved from iptables to nftables. If it does not work on your build, stop forcing old scripts into the firewall and use nlbwmon.
I am even more cautious with luci-app-trafficctl. The name turns up in third-party feeds and build collections, but package purpose and compatibility can vary. If your configured feed does not list it, do not download a random build and hand it control of the firewall.
How do you fix missing or inaccurate nlbwmon data?
Work through the accounting path in order. Reinstalling the LuCI package first only replaces the page that displays the data.
- Confirm the daemon is running.
/etc/init.d/nlbwmon status
pgrep -af nlbwmon
The first command reports service state. The second shows the running process and its arguments.
- Read the log around the failure.
logread -e nlbwmon
Look for database, permission, protocol database, and netlink errors. Read the exact line before changing the configuration.
- Inspect the raw records.
nlbw -c show
If raw records exist but LuCI is empty, the collection side works. The fault is in the selected period, browser session, or frontend.
- Check local-network coverage.
uci show nlbwmon
ip -br addr
Compare the configured networks with the addresses actually assigned to the router. A guest or IoT subnet cannot be attributed correctly if nlbwmon does not consider it local.
- Check flow offloading.
uci -q get 'firewall.@defaults[0].flow_offloading'
uci -q get 'firewall.@defaults[0].flow_offloading_hw'
Enabled output tells you that flows may leave the normal software path. If totals are consistently low, disable offloading temporarily in LuCI, restart the firewall, and repeat a controlled transfer.
Restarting the firewall interrupts active connections, so do that during a maintenance window. If disabling offloading makes the counters line up, you have found the trade-off. Decide whether per-device accuracy matters more than the throughput benefit on that router.
Do not leave offloading disabled because an old guide declared it bad. Prove that it changes your result first. On routers struggling for throughput, packet steering across CPU cores may also matter, but it solves a performance problem rather than an accounting problem.
How do you stop nlbwmon history disappearing?

Find where the database lives and whether that filesystem survives a reboot. History stored only in RAM is disposable, regardless of how polished the graph looks.
Also check how often the daemon commits in-memory counters. A crash between commits can lose recent data even when the database itself is persistent. A longer interval reduces storage writes but increases the uncommitted window.
Before a firmware upgrade, use the nlbwmon backup function if your LuCI build provides one. Also keep a normal OpenWrt configuration backup. Do not assume a sysupgrade image will preserve an application database merely because it preserves UCI configuration.
After the upgrade, confirm that the package is installed, the database path exists, and the service starts. Package restoration and data restoration are separate jobs.
When should you skip nlbwmon?
Skip it if you need to know which remote domain, socket, or process caused the traffic. Encrypted traffic and router-level forwarding counters do not provide that detail.
Use packet capture for packet-level proof. On suitable hardware, monitoring OpenWrt traffic with eBPF gives you deeper kernel visibility without trying to turn a LuCI accounting page into an observability system.
Also skip nlbwmon as your only monitor when you need whole-interface history. Install vnStat for that job. If you need live contention, use a compatible wrtbwmon build or LuCI’s real-time graphs.
For ordinary per-device accounting, though, luci-app-nlbwmon remains the sensible first install. Understand its conntrack boundary, store the database somewhere that survives, and compare like periods. Most reports of broken numbers start with one of those three being ignored.
FAQ
Can nlbwmon limit a device after it reaches a quota?
No. nlbwmon records usage but does not enforce a cap by itself. Enforcement needs firewall rules, traffic shaping, or another quota system that can act on the device identity.
Keep accounting and enforcement separate until both are proven. A broken counter should not become a broken internet connection for the wrong host.
Will nlbwmon work on a dumb access point?
Not as a useful internet-usage monitor unless that access point is also the routed observation point. A dumb access point bridges client traffic, while conntrack accounting normally happens on the gateway doing the routing and firewall work.
Install nlbwmon on the main OpenWrt gateway. That is where the flows pass through the accounting path.
Can nlbwmon identify websites or applications?
Only in a limited way. Protocol labels can classify some traffic, but encryption, shared hosting, content delivery networks, and changing application behavior make fine-grained labels unreliable.
If you need to prove which endpoint a device contacted, use DNS logs, socket data, or packet capture. Do not infer a website from a bandwidth total.
Does nlbwmon handle multiple WAN connections?
It can still attribute local flows, but that does not mean every per-host row cleanly maps to one WAN provider. Policy routing, tunnels, failover, and load balancing can move traffic through different paths.
For per-WAN totals, monitor each WAN interface separately. Use nlbwmon for the host view and interface counters for the uplink view.
Why does one phone appear as several devices?
The phone may be changing its MAC address, using several IPv6 addresses, or moving between network segments. Static DHCP leases help only when the client presents a stable identity.
Check the MAC and address columns before merging records by hostname. Hostnames are labels, not proof that two rows belong to the same device.
