Long-Term Network Bandwidth Monitoring with vnStat on Linux: Track, Analyze, and Export Usage Reports

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

Context & Why You Need vnStat

Most network monitoring tools show you what’s happening right now — packet rates, active connections, current throughput. That’s useful for diagnosing a spike, but it tells you nothing about your bandwidth consumption over the past three months. When a cloud provider sends a surprise overage bill, or your ISP throttles you without warning, you need historical data — not a live graph.

That’s the gap vnStat fills. It runs as a lightweight daemon, reading interface statistics from the kernel’s own counters and accumulating them into a local database. Query usage by hour, day, month, or year — no packet capturing required, and no root access needed just to pull a report.

Running vnStat in production — on both VPS instances and bare-metal servers — has been reliable enough that I’ve stopped thinking about it. After a year on a busy web server handling ~50K requests/day, I had clean per-month traffic data I could cross-reference against CDN invoices to catch billing discrepancies. The resource footprint is tiny: around 1–2 MB of RAM, with periodic disk writes.

One thing that sets vnStat apart from alternatives like iftop or nethogs: it survives reboots. Because it reads kernel counters rather than sniffing the wire, it reconstructs totals correctly even after an interface restart — as long as the daemon was running when the counter rolled over.

Installation

vnStat packages are available in every major distro’s default repositories. No PPAs or third-party sources needed.

Debian / Ubuntu

sudo apt update
sudo apt install vnstat -y

RHEL / CentOS / Rocky Linux / AlmaLinux

# Enable EPEL first if not already done
sudo dnf install epel-release -y
sudo dnf install vnstat -y

Arch Linux

sudo pacman -S vnstat

Before going further, check your version. vnStat 2.x (released 2019) overhauled the database format and added JSON export — two things this guide depends on:

vnstat --version

Expected output looks like:

vnStat 2.12 by Teemu Toivola <tst at iki dot fi>

Configuration

Starting the Daemon

The daemon — vnstatd — handles all the data collection. Enable and start it with systemd:

sudo systemctl enable vnstat
sudo systemctl start vnstat
sudo systemctl status vnstat

By default, it polls each configured interface every 5 minutes. Data lands in /var/lib/vnstat/ — one database file per interface.

Adding Interfaces to Monitor

The daemon auto-detects interfaces on startup. If you add a new interface later — a VPN tunnel, a second NIC — you can register it manually:

# List available interfaces
ip link show

# Add a specific interface manually
sudo vnstat --add -i eth0

# Or for cloud instances where the interface name may differ
sudo vnstat --add -i ens3

After adding, wait a few minutes for the daemon to start collecting. The first query might come back empty or sparse — that’s normal. Give it 15–30 minutes before expecting meaningful hourly data.

Editing the Configuration File

The main config lives at /etc/vnstat.conf. A few settings worth adjusting:

sudo nano /etc/vnstat.conf

Key options to review:

# Interface to monitor by default (useful when you have only one)
Interface "eth0"

# How often the daemon polls (in seconds, default 300)
UpdateInterval 300

# How many days/months/years to keep in the database
SaveInterval 5
OfflineSaveInterval 30

# Maximum days to store in daily stats (0 = unlimited)
DailyDays 365

# Maximum months
MonthlyMonths 25

On any server I plan to run for more than a year, I set DailyDays to 730 and MonthlyMonths to 36. The database stays compact — under 500 KB even with three years of daily records. No reason to throw away history.

After editing, restart the daemon:

sudo systemctl restart vnstat

Verification & Monitoring

Basic Usage Queries

Once the daemon has been running for a while, start querying. Running vnstat with no arguments prints a summary for all monitored interfaces:

vnstat

Sample output:

                      rx      /      tx      /     total    /   estimated
 eth0:
       2024-05     45.23 GiB  /   12.67 GiB  /   57.90 GiB  /   61.20 GiB
       2024-06      3.10 GiB  /    0.87 GiB  /    3.97 GiB  /   63.52 GiB
     estimated     63.52 GiB  /   17.83 GiB  /   81.35 GiB

The estimated row extrapolates the current month’s usage — useful for spotting whether you’ll blow past a monthly cap before the billing cycle resets.

Detailed Per-Period Reports

# Daily breakdown (last 30 days)
vnstat -d

# Hourly breakdown (last 24 hours)
vnstat -h

# Monthly summary
vnstat -m

# Yearly totals
vnstat -y

# 5-minute intervals (most granular, last few hours)
vnstat -5

# Specify interface explicitly
vnstat -i eth0 -m

Live Traffic Rate

Need a quick read on current throughput? Skip iftop — vnStat’s built-in live mode is good enough for a simple sanity check:

# Live view — updates every second
vnstat -l -i eth0

Lighter than iftop, and perfectly fine for a “is traffic flowing?” gut-check.

Exporting Data to JSON

One feature I keep coming back to: JSON export. Pipe the output straight into a script or dashboard — no regex, no fragile text parsing.

# Full JSON dump for an interface
vnstat -i eth0 --json

# Monthly data only in JSON
vnstat -i eth0 --json m

# Daily data in JSON
vnstat -i eth0 --json d

A minimal Python script to parse monthly totals and print them:

import subprocess
import json

result = subprocess.run(
    ["vnstat", "-i", "eth0", "--json", "m"],
    capture_output=True, text=True
)
data = json.loads(result.stdout)

for entry in data["interfaces"][0]["traffic"]["month"]:
    year = entry["date"]["year"]
    month = entry["date"]["month"]
    rx_gb = entry["rx"] / 1024**3
    tx_gb = entry["tx"] / 1024**3
    print(f"{year}-{month:02d}  RX: {rx_gb:.2f} GB  TX: {tx_gb:.2f} GB")

Setting Up a Simple Bandwidth Alert

Got a metered VPS with a monthly cap? Wire up this cron script before you hit your limit. It fires an email when monthly RX crosses 80 GB:

#!/bin/bash
# /usr/local/bin/check_bandwidth.sh

LIMIT_GB=80
RX_BYTES=$(vnstat -i eth0 --json m | python3 -c "
import sys, json
d = json.load(sys.stdin)
entries = d['interfaces'][0]['traffic']['month']
if entries: print(entries[-1]['rx'])
else: print(0)
")

RX_GB=$(echo "scale=2; $RX_BYTES / 1073741824" | bc)

if (( $(echo "$RX_GB > $LIMIT_GB" | bc -l) )); then
    echo "WARNING: Monthly RX is ${RX_GB} GB (limit: ${LIMIT_GB} GB)" \
    | mail -s "Bandwidth Alert: eth0" [email protected]
fi

Add it to cron to run daily:

crontab -e
# Add:
0 9 * * * /usr/local/bin/check_bandwidth.sh

Viewing Historical Data After Database Migration

Moving servers or doing a clean reinstall? Copy /var/lib/vnstat/ to the new machine before starting the daemon and your full history comes with it. The data format is portable across identical vnStat versions.

Jumping from 1.x to 2.x is a different story — you’ll need to export and reimport. The vnStat 2.x package usually ships a migration tool for exactly this.

Practical Tips from Ongoing Use

  • Pin your interface names. If your cloud provider renames interfaces on reboot (e.g., ens3 becomes ens4), vnStat creates a new database for the new name and your history splits in two. Use udev rules to lock the name, or check your provider’s docs.
  • Monitor multiple interfaces separately. On a server with both a public and a private NIC, track them independently. Internal traffic — say, between Docker containers on a private VLAN — can inflate totals if you’re watching the wrong interface.
  • Pair with ss or nethogs for per-process attribution. vnStat gives you totals per interface, not per process. When a number spikes, use ss -tulnp or nethogs to find what’s responsible, then cross-reference vnStat’s hourly data to see when it started.
  • Check the database directory periodically. With default settings, /var/lib/vnstat/ stays well under 10 MB even after years of data. Unexpected growth usually means a misconfigured Interface entry is tracking a loopback or virtual interface with inflated counters.
Share: