1. 定位

vnstat 是一个面向 Linux / BSD 的轻量级网络流量统计工具。它的核心目标不是实时抓包,而是长期记录网卡维度的接收流量 rx、发送流量 tx 和总量 total,适合 VPS、家用服务器、NAS、路由器、跳板机、代理机等场景。

官方文档对它的定位很明确:vnstat 使用内核提供的网络接口统计信息作为数据来源,不 sniff traffic,因此 CPU 和内存开销非常低,流量越大也不会像抓包工具那样显著增加处理成本。

它和常见工具的区别:

工具主要用途是否长期记账粒度
vnstat按网卡长期统计流量是5 分钟 / 小时 / 天 / 月 / 年
iftop实时连接流量否连接 / 主机
nethogs按进程看实时流量否进程
tcpdump抓包分析否packet
node_exporter主机指标采集依赖 Prometheus多指标

如果问题是“这台机器本月用了多少流量”,优先用 vnstat。如果问题是“哪个进程正在跑流量”,用 nethogs。如果问题是“这个连接具体发了什么包”,用 tcpdump。

2. 本机环境实测

当前系统已经安装 vnstat:

vnstat --version

输出:

vnStat 2.13 by Teemu Toivola <tst at iki dot fi> (SQLite 3.51.2)

systemd 服务状态:

systemctl status vnstat --no-pager

关键输出:

● vnstat.service - vnStat network traffic monitor
     Loaded: loaded (/usr/lib/systemd/system/vnstat.service; enabled; preset: disabled)
     Active: active (running) since Thu 2026-05-28 08:56:56 CST
   Main PID: 271177 (vnstatd)
      Memory: 1.1M
         CPU: 456ms
     CGroup: /system.slice/vnstat.service
             └─271177 /usr/sbin/vnstatd --nodaemon

服务日志显示,首次启动时自动发现了 5 个接口:

Interface "enp2s0" added with 1000 Mbit bandwidth limit.
Interface "enp3s0" added with 1000 Mbit bandwidth limit.
Interface "wg0" added with 1000 Mbit bandwidth limit.
Interface "tun-int" added with 10000 Mbit bandwidth limit.
Interface "tun-ext" added with 10000 Mbit bandwidth limit.
Info: Data retention: 48 5MinuteHours, 4 HourlyDays, 62 DailyDays, 25 MonthlyMonths, -1 YearlyYears, 20 TopDayEntries
Info: Monitoring (5): wg0 (1000 Mbit) tun-int (10000 Mbit) tun-ext (10000 Mbit) enp3s0 (1000 Mbit) enp2s0 (1000 Mbit)

当前可用接口:

vnstat --iflist
Available interfaces: enp2s0 (1000 Mbit) enp3s0 (1000 Mbit) wg0 tun-int (10000 Mbit) tun-ext (10000 Mbit)

数据库中的接口:

vnstat --dbiflist
Interfaces in database: enp2s0 enp3s0 tun-ext tun-int wg0

默认概览:

vnstat
                      rx      /      tx      /     total    /   estimated
 enp2s0:
       2026-05      7.15 MiB  /    1.92 MiB  /    9.07 MiB  /  482.67 MiB
         today      7.15 MiB  /    1.92 MiB  /    9.07 MiB  /   20.56 MiB

 enp3s0:
       2026-05    218.99 MiB  /   34.28 MiB  /  253.27 MiB  /   13.17 GiB
         today    218.99 MiB  /   34.28 MiB  /  253.27 MiB  /  574.34 MiB

 tun-ext:
       2026-05    150.61 KiB  /  150.61 KiB  /  301.21 KiB  /   15.54 MiB
         today    150.61 KiB  /  150.61 KiB  /  301.21 KiB  /     681 KiB

 tun-int:
       2026-05     35.08 KiB  /   35.08 KiB  /   70.16 KiB  /    3.59 MiB
         today     35.08 KiB  /   35.08 KiB  /   70.16 KiB  /     158 KiB

 wg0:
       2026-05     42.04 MiB  /    6.04 MiB  /   48.08 MiB  /    2.50 GiB
         today     42.04 MiB  /    6.04 MiB  /   48.08 MiB  /  109.03 MiB

> 本机数据是 2026-05-28 当天刚开始采集的结果,因此历史记录较短。vnstat 的价值会随着 daemon 长期运行而体现出来。

3. 安装与启动

Fedora / RHEL 系

sudo dnf install vnstat
sudo systemctl enable --now vnstat

Debian / Ubuntu 系

sudo apt update
sudo apt install vnstat
sudo systemctl enable --now vnstat

Arch Linux

sudo pacman -S vnstat
sudo systemctl enable --now vnstat

源码安装

官方源码构建的最短路径:

./configure --prefix=/usr --sysconfdir=/etc
make
sudo make install
sudo systemctl enable --now vnstat

源码安装需要 C compiler、SQLite3 library/development files。vnstati 图片输出还需要 libgd。

4. 常用命令

4.1 查看概览

vnstat

不指定接口时,vnstat 会按配置和数据库自动选择默认接口;如果有多接口,会显示所有已监控接口的汇总。

4.2 指定接口

vnstat -i enp3s0
vnstat -i wg0

适合多网卡、WireGuard、TUN/TAP、Docker host 网络等场景。

4.3 5 分钟、小时、天、月、年

vnstat -5
vnstat -h
vnstat -d
vnstat -m
vnstat -y

示例:查看 enp3s0 最近 8 小时:

vnstat -i enp3s0 -h 8

本机输出:

 enp3s0  /  hourly

         hour        rx      |     tx      |    total    |   avg. rate
     ------------------------+-------------+-------------+---------------
     2026-05-28
         08:00      5.06 MiB |  935.88 KiB |    5.97 MiB |   13.91 kbit/s
         09:00     85.59 MiB |   19.16 MiB |  104.75 MiB |  244.09 kbit/s
         10:00    128.34 MiB |   14.20 MiB |  142.54 MiB |  569.40 kbit/s

4.4 实时速率

vnstat -l
vnstat -l -i enp3s0

-l 是 live mode,适合临时观察实时收发速率。它仍然不是抓包,只是更频繁地读取接口计数器。

4.5 短时间采样

vnstat -tr
vnstat -tr 10 -i enp3s0

-tr 会在指定秒数内采样并计算平均速率,默认采样时间由 Sampletime 控制,本机默认配置为 5 秒。

4.6 Top days

vnstat -t
vnstat -i enp3s0 -t 20

用于查历史峰值日期,比如定位某天异常下载、备份或同步流量。

4.7 JSON 输出

vnstat --json
vnstat --json s
vnstat -i enp3s0 --json d 30

--json s 会输出 summary,包含每个接口最近 2 条 5 分钟、小时、天、月、年数据。所有 traffic 数值默认以 bytes 表示,适合脚本、Telegraf、Prometheus exporter 或自写 API 读取。

4.8 XML / oneline 输出

vnstat --xml
vnstat --oneline

--oneline 适合非常简单的 shell 脚本;复杂集成建议使用 JSON。

4.9 95th percentile

vnstat --95th

95th percentile 常用于带宽计费场景。注意官方 man page 说明:如果要覆盖整个月,5MinuteHours 需要至少保存 744 小时的 5 分钟数据;默认 48 小时不够,只能算当前保留窗口内的数据。

5. 接口管理

5.1 添加接口

新版 vnstatd 在无数据库或配置允许时会自动添加可见接口。手动添加:

sudo vnstat --add -i eth0
sudo systemctl restart vnstat

如果 RescanDatabaseOnSave 开启,daemon 可在下次保存周期后自动发现数据库新增接口,不一定需要重启。

5.2 删除不需要监控的接口

sudo vnstat --remove -i tun-ext

删除前建议先确认:

vnstat --dbiflist

5.3 给接口设置别名

sudo vnstat -i enp3s0 --setalias lan
vnstat -i lan

别名适合接口名可能变化的场景,例如 USB 网卡、虚拟网卡或容器环境。配置项 InterfaceMatchMethod 控制接口名与别名的匹配方式。

5.4 合并接口查询

vnstat -i enp3s0+wg0

这只是在查询时合并展示,不会改变数据库原始记录。

6. 配置文件

主配置文件:

/etc/vnstat.conf

数据库目录:

/var/lib/vnstat/

本机实际文件:

ls -l /etc/vnstat.conf /var/lib/vnstat
-rw-r--r--. 1 root   root   6552 Jan 17 08:00 /etc/vnstat.conf

/var/lib/vnstat:
total 68
-rw-r--r--. 1 vnstat vnstat 69632 May 28 10:35 vnstat.db

常用配置项:

配置项默认值作用
DatabaseDir/var/lib/vnstatSQLite 数据库目录
UpdateInterval20 或发行版默认daemon 多久读取一次接口计数器
PollInterval5多久检查接口上下线
SaveInterval5多久把缓存写入数据库
5MinuteHours485 分钟粒度保留多少小时
HourlyDays4小时粒度保留多少天
DailyDays62日粒度保留多少天
MonthlyMonths25月粒度保留多少个月
YearlyYears-1年粒度保留多少年,-1 表示无限
MonthRotate1每月从几号开始统计,适合账单周期
MaxBandwidth1000异常流量过滤的带宽上限,单位 Mbit
BandwidthDetection1尝试自动检测接口带宽
UseUTC0是否用 UTC 存储时间
DatabaseWriteAheadLogging0是否启用 SQLite WAL

> 如果运营商或 VPS 的流量账单不是自然月,例如每月 7 日重置,可以把 MonthRotate 设置为 7。这个设置只影响之后写入的数据,不会重算已有历史。

6.1 修改历史数据保留策略

vnstat 的历史数据保留时间由 /etc/vnstat.conf 中的 5MinuteHours、HourlyDays、DailyDays 等配置控制。默认策略比较保守,适合低占用长期运行;如果希望后续能查看更长时间的小时级或 5 分钟级趋势,可以显式调大。

本机调整目标:

5MinuteHours   2160
HourlyDays      365
DailyDays       365

其中 5MinuteHours 2160 等价于 24 * 90,表示保留 90 天的 5 分钟粒度数据。配置文件不支持写表达式,因此不能写成 24*90,必须写最终整数 2160。

实际执行命令:

sudo cp /etc/vnstat.conf /etc/vnstat.conf.bak.$(date +%F-%H%M%S)

sudo sed -i \
  -e 's/^;*5MinuteHours.*/5MinuteHours   2160/' \
  -e 's/^;*HourlyDays.*/HourlyDays      365/' \
  -e 's/^;*DailyDays.*/DailyDays       365/' \
  /etc/vnstat.conf

sudo systemctl restart vnstat

验证配置文件:

grep -nE '^(5MinuteHours|HourlyDays|DailyDays)' /etc/vnstat.conf

本机输出:

111:5MinuteHours   2160
112:HourlyDays      365
113:DailyDays       365

验证 daemon 已读取新策略:

journalctl -u vnstat -n 40 --no-pager

关键输出:

Info: Data retention: 2160 5MinuteHours, 365 HourlyDays, 365 DailyDays, 25 MonthlyMonths, -1 YearlyYears, 20 TopDayEntries
Info: Monitoring (5): wg0 (1000 Mbit) tun-int (10000 Mbit) tun-ext (10000 Mbit) enp3s0 (1000 Mbit) enp2s0 (1000 Mbit)

> 增大保留时间只影响之后的数据保留,已经被 vnstat 清理掉的历史明细无法恢复。已有的日、月、年汇总仍按数据库中现存数据继续保留。

7. 告警用法

vnstat --alert 可以按小时、天、月、年或 95th percentile 做阈值判断,并用退出码配合 cron、systemd timer、脚本或监控系统。

示例:如果 enp3s0 今日 total 预计超过 10 GiB,则输出告警并返回非零状态:

vnstat -i enp3s0 --alert 2 2 d total_estimate 10 GiB

参数含义:

位置示例值含义
output2只有超过 estimate limit 时输出
exit2超过 estimate limit 时返回 1,否则返回 0
typed按 day 比较
conditiontotal_estimate使用 total 的估算值
limit10阈值数字
unitGiB阈值单位

更适合脚本的写法:

if ! vnstat -i enp3s0 --alert 0 2 m total 1000 GiB; then
  echo "monthly traffic exceeded"
fi

8. 底层技术细节

8.1 数据不是来自抓包

vnstat 的数据来自内核接口计数器。在 Linux 上,常见来源包括:

/proc/net/dev
/sys/class/net/<iface>/statistics/rx_bytes
/sys/class/net/<iface>/statistics/tx_bytes

这类计数器由内核网络栈或网卡驱动维护,记录接口自启动以来累计收发的字节数。vnstatd 周期性读取这些累计值,和上一次读取值做差,得到当前时间片内新增的 rx / tx。

用本机 enp3s0 举例:

ip -s link show enp3s0

输出中的 RX bytes / TX bytes 是内核计数器:

RX:   bytes  packets errors dropped  missed   mcast
    35225303975 25292897      0       0       0    5008
TX:   bytes  packets errors dropped carrier collsns
     3189468298  7368624      0    2983       0       0

vnstat 记录的是增量,不是直接保存这两个累计值作为最终结果。

8.2 daemon 与 CLI 分工

vnstat 实际上分为两个角色:

组件作用
vnstatd后台 daemon,负责读取接口计数器、缓存、写 SQLite
vnstat查询 CLI,负责从数据库读取并格式化输出
vnstati可选图片输出工具,生成 PNG 图

官方 man page 说明:vnstatd 大部分时间处于 sleep 状态,只在更新、轮询或保存周期醒来工作。因此它非常适合长期运行。

本机 systemd 启动的命令是:

/usr/sbin/vnstatd --nodaemon

在 systemd 服务里使用 --nodaemon 很常见,因为 systemd 自己负责进程管理,不需要 daemon 再 fork 到后台。

8.3 时间窗口与聚合

vnstatd 会把短周期采样聚合成多种分辨率:

5-minute -> hour -> day -> month -> year

默认保留策略来自配置:

5MinuteHours   48
HourlyDays      4
DailyDays      62
MonthlyMonths  25
YearlyYears    -1
TopDayEntries  20

这意味着:

  • 5 分钟明细只保留最近 48 小时
  • 小时明细只保留最近 4 天
  • 日统计保留 62 天
  • 月统计保留 25 个月
  • 年统计无限保留

如果你要做更细粒度的长期图表,需要调整保留策略。例如保留 90 天 5 分钟数据,同时把小时和日统计都扩展到 365 天:

5MinuteHours   2160
HourlyDays      365
DailyDays       365

但这样会增加 SQLite 数据库体积。vnstat 数据量仍然很小,只是没有必要无限保留高分辨率数据。调整后要重启 vnstat,并通过 journalctl -u vnstat 确认 daemon 日志里的 Data retention 已更新。

8.4 SQLite 数据库结构

本机 /var/lib/vnstat/vnstat.db 是 SQLite 数据库。当前系统没有安装 sqlite3 CLI,因此使用 Python 标准库只读查看 schema:

python3 - <<'PY'
import sqlite3

path = "/var/lib/vnstat/vnstat.db"
con = sqlite3.connect(f"file:{path}?mode=ro", uri=True)
cur = con.cursor()

print("tables:")
for (name,) in cur.execute("select name from sqlite_master where type='table' order by name"):
    print("-", name)

print("\\ninterfaces:")
for row in cur.execute("select id,name,alias,active,created,updated from interface order by name"):
    print(row)

con.close()
PY

输出:

tables:
- day
- fiveminute
- hour
- info
- interface
- month
- top
- year

interfaces:
(1, 'enp2s0', None, 1, '2026-05-28 08:56:57', '2026-05-28 10:35:00')
(2, 'enp3s0', None, 1, '2026-05-28 08:56:57', '2026-05-28 10:35:00')
(5, 'tun-ext', None, 1, '2026-05-28 08:56:57', '2026-05-28 10:35:00')
(4, 'tun-int', None, 1, '2026-05-28 08:56:57', '2026-05-28 10:35:00')
(3, 'wg0', None, 1, '2026-05-28 08:56:57', '2026-05-28 10:35:00')

核心表结构可以简化理解为:

interface:
  id, name, alias, active, created, updated,
  rxcounter, txcounter, rxtotal, txtotal

fiveminute / hour / day / month / year / top:
  id, interface, date, rx, tx

其中 interface.rxcounter / txcounter 用于记录上次读取到的内核累计计数器,rxtotal / txtotal 是总累计值,各时间窗口表保存聚合后的 rx / tx。

8.5 计数器回绕、重启与异常过滤

网卡计数器可能遇到几个问题:

  • 系统重启后计数器从 0 开始
  • 32-bit 计数器在高流量下可能回绕
  • 虚拟接口重建后计数器清零
  • 驱动或系统异常导致计数器突变

vnstatd 会尝试根据启动时间、接口状态和计数器变化判断是否需要同步,而不是简单地把负数差值当作真实流量。配置项 64bitInterfaceCounters 默认自动检测;MaxBandwidth / MaxBW<iface> 用于过滤不合理的突增值。

这也是为什么在高速接口或虚拟接口较多的机器上,需要关注日志:

journalctl -u vnstat --since today --no-pager

如果日志频繁出现超出 bandwidth limit 或接口上下线,就要检查 MaxBandwidth、接口实际速率和虚拟接口生命周期。

8.6 时间与时区

默认 UseUTC 0 时,数据库使用本地时区写入。修改系统时区或系统时间不会重算旧数据。如果系统时间回拨到已经出现过的时间段,新的流量会累加到已有时间段。

建议:

  • 普通本地服务器:保留 UseUTC 0
  • 跨时区统一报表:考虑 UseUTC 1
  • 没有 RTC 的嵌入式设备:保留或调大 TimeSyncWait

9. 可视化与集成

Vault 里已有一篇 vnstat 流量监控工具使用指南 + Grafana 可视化集成,重点讲了 Grafana 集成。这里补充几个集成路径:

9.1 vnstati 生成图片

如果安装了 vnstati 和 libgd:

vnstati -s -i enp3s0 -o summary.png
vnstati -d -i enp3s0 -o daily.png
vnstati -m -i enp3s0 -o monthly.png

适合静态 Web 页面、邮件日报或轻量 dashboard。

9.2 JSON + 脚本

vnstat -i enp3s0 --json s

脚本里可以读取 interfaces[].traffic.day[-1].rx / tx,转换成 MiB/GiB 后发通知。

9.3 Prometheus / Grafana

常见方案:

  • vnstat-exporter 读取 vnstat --json 或数据库,暴露 Prometheus metrics
  • Prometheus 定期 scrape
  • Grafana 展示日/月/接口维度流量

这种方式适合已经有 Prometheus 栈的环境。没有 Prometheus 时,直接用 JSON 输出 + cron 也足够。

9.4 Telegraf / InfluxDB

Telegraf 可以用 inputs.exec 定期执行:

[inputs.exec](/posts/inputs.exec/)
  commands = ["/usr/bin/vnstat -i enp3s0 --json"]
  timeout = "10s"
  data_format = "json"

然后写入 InfluxDB,再由 Grafana 查询。

10. 典型使用场景

10.1 VPS 月流量防超

vnstat -m
vnstat --alert 2 2 m total_estimate 950 GiB

如果 VPS 套餐每月 1 TiB,可以在估算值接近 950 GiB 时告警。

10.2 WireGuard / 代理链路流量

vnstat -i wg0
vnstat -i tun-int
vnstat -i tun-ext

适合观察 VPN、TUN 代理或透明网关流量。注意虚拟接口流量可能和物理网卡流量重复计数,例如 wg0 上的加密隧道流量最终也会经过 enp3s0 出口。

10.3 多网卡主机

vnstat --iflist
vnstat --dbiflist
vnstat -i enp2s0
vnstat -i enp3s0

如果一个接口是 LAN,另一个是 WAN,建议设置 alias:

sudo vnstat -i enp2s0 --setalias lan
sudo vnstat -i enp3s0 --setalias wan

10.4 自然月之外的账单周期

例如账单每月 7 日重置:

MonthRotate 7

修改后重启:

sudo systemctl restart vnstat

11. 排障

11.1 没有数据

检查服务:

systemctl status vnstat --no-pager
journalctl -u vnstat --since today --no-pager

检查接口是否存在:

vnstat --iflist
ip link

检查数据库是否有接口:

vnstat --dbiflist

如果数据库没有接口:

sudo vnstat --add -i eth0
sudo systemctl restart vnstat

11.2 接口名变了

USB 网卡、虚拟网卡、容器网络重建后可能出现接口名变化。处理方式:

vnstat --iflist
vnstat --dbiflist
sudo vnstat --add -i newname
sudo vnstat --remove -i oldname

如果只是显示名不友好,优先用 alias,不要随意改系统网卡名。

11.3 统计值异常偏大

排查方向:

  • 是否同时统计了物理接口和隧道接口,导致理解上重复
  • 是否虚拟接口重建造成计数器重置
  • MaxBandwidth 是否设置过低或过高
  • 系统时间是否跳变
  • 是否存在 32-bit 计数器回绕问题

查看日志:

journalctl -u vnstat --since "2026-05-28" --no-pager

11.4 权限问题

vnstat 查询数据库通常不需要 root,但手动添加、删除接口可能需要 root。数据库默认归属:

/var/lib/vnstat/vnstat.db -> vnstat:vnstat

如果 exporter 或 Telegraf 读取失败,需要检查执行用户是否能调用 vnstat --json,不要直接给数据库乱改权限。

12. 实践建议

  • 长期记账使用 vnstatd,不要用 cron 反复跑 vnstat 替代 daemon。
  • 多接口环境先执行 vnstat --iflist 和 vnstat --dbiflist,确认“系统可见接口”和“数据库监控接口”是否一致。
  • 虚拟接口、VPN、容器场景要注意重复统计:物理出口和隧道接口都可能记录同一业务流量的不同层面。
  • 做月流量账单时关注 MonthRotate,不要默认所有账单都按自然月计算。
  • 做 95th percentile 时调大 5MinuteHours,否则默认 48 小时无法覆盖整月。
  • 自动化集成优先使用 --json,不要解析普通文本输出。
  • 高速接口或异常值较多时检查 MaxBandwidth 和日志。

13. 参考资料