Skip to content

Repository files navigation

DiskWatch

Single-host disk diagnostics in your terminal. The terminal you open when the disk light won't stop blinking — before you reach for iostat, iotop, smartctl, lsblk, df, du, and a panic.

crates.io Release Packaging status Platform License

Sibling to NetWatch and SysWatch. Same chrome, same palette, same keys.

DiskWatch Dense — six boxes on one screen

Dense

diskwatch --dense

Six boxes, one screen, no chrome rows — every keybind, sort state and page counter lives in a box border.

Box Shows
io Mirrored read/write throughput on a shared time axis. Peak, average, iops split by direction, mean request size, utilisation, await, p99.
devices Per-device read/write, utilisation, size, type, 48s sparkline. Stacked devices (md, dm, LVM, LUKS) are listed but excluded from totals.
latency IO completion histogram, seven buckets from <0.1ms to >50ms, with p50/p95/p99 and the share of ops past 10 ms. Bars are coloured by bucket, not by count, so the tail is visible before it fills.
volumes Capacity meters with a days-to-full projection from observed growth. Silent when a volume is flat or shrinking.
smart Health, wear, host writes, temperature, spare.
files Busiest paths by event rate. Filter with /, sort with s.

Falls back to a compact screen below 104×32, keeping the mirror and the percentiles.

Tabs

DiskWatch — Overview, Devices, Volumes, FS, IO, SMART, Hot Files, Insights

# Tab Shows Replaces
1 Overview KPI tiles, device summary, aggregate IO, capacity bar
2 Devices model, firmware, serial, used %, SMART, per-device detail lsblk, nvme list, diskutil list, hdparm -I
3 Volumes APFS containers + roles; mdraid members, [UUUU] state, resync progress lvs, vgs, mdadm --detail, diskutil apfs list
4 FS mounts with usage bars, thresholds, system/user/removable df -h, df -i, mount, findmnt
5 IO per-device throughput, 48s sparkline, p50/p99 read and write iostat -x 1
6 SMART full NVMe/ATA attribute tables when smartctl is present smartctl -A, nvme smart-log
7 Hot Files paths by event rate (FSEvents / inotify) fatrace, fs_usage
8 Insights capacity, SMART, wear, temperature, latency and hot-file anomalies

Lite

diskwatch --lite

80×24, six keys, no tabs: read and write throughput, a capacity line that answers how long have I got, and the busiest files. Sized for a tmux split or an SSH session to a NAS. Same grid and keys as netwatch --lite.

Install

brew install diskwatch                # macOS / Linux
nix-shell -p diskwatch                # NixOS / Nix
paru -S diskwatch                     # Arch
cargo install diskwatch               # anywhere with Rust
x eget use matthart1983/diskwatch     # prebuilt release binary

Or a pre-built binary from Releases — Linux and macOS, x86_64 and aarch64, plus static musl builds.

No system dependencies on Linux. macOS uses the preinstalled ioreg, diskutil and system_profiler. Optional: smartmontools for full SMART attribute tables; without it the SMART tab falls back to the basic verified/failing flag.

Nix and Arch packages are maintained by community packagers — file packaging issues with them, diskwatch bugs here. Repology shows which are current.

From source
git clone https://github.com/matthart1983/diskwatch.git && cd diskwatch
cargo build --release
./target/release/diskwatch

Rust 1.75+.

Keys

Key Action
18 Switch tabs
V Cycle view: full → lite → dense
L Jump straight to Lite
j k Move selection
/ Filter files (Lite, Dense)
s Cycle file sort (Dense)
p Pause / resume
, Settings — columns, temperature unit, SMART interval, theme, view
r Force a SMART refresh
? Help
q / Esc Quit

Options

Flag
--dense Start in Dense (--v2, --btop still work)
--lite Start in Lite
--view full, lite, dense
--tab Start on a named tab
--theme terminal (default), dark, light, ocean, solarized, dracula, nord
--graph bars (default) or dots for btop-style braille
--graph-fade btop's brightness gradient and dot grid — needs an RGB theme, so pair with --theme dark
--no-graph-fade Turn the gradient off, against a config file that turns it on
--watch PATH Watch this path in Hot Files instead of the defaults. Repeatable
--watch-add PATH Watch this path in addition to the defaults. Repeatable
--config PATH Read this config file instead of the default location
--no-config Ignore any config file and run on built-in defaults
--write-config Write a commented config file listing every setting, then exit
--diag Print collected state and exit, no TUI

diskwatch defers to your terminal's palette by default. It pins no colours of its own: every slot resolves to an ANSI entry and foreground/background use Reset, so a terminal profile, pywal, matugen or a system-wide rice carries straight through and diskwatch sits beside your other tools instead of fighting them.

Pass --theme dark for diskwatch's own designed palette, or any of the other built-ins. Those pin real RGB, which is what --graph-fade needs to fade through — a 16-colour palette has no intermediate shades, so the gradient does nothing under the default.

Theme and graph style are also live in the settings overlay. Changes there last for the session; the config file below is what makes one stick.

Config

diskwatch runs with zero config. If you want one anyway:

diskwatch --write-config

writes a commented file listing every setting at its default, to $XDG_CONFIG_HOME/diskwatch/config.toml (~/.config/diskwatch/config.toml if that's unset — macOS included). $DISKWATCH_CONFIG or --config PATH points elsewhere; --no-config ignores it entirely.

theme = "nord"
view = "dense"
tab = "hot"
smart_interval_secs = 60
temp_unit = "fahrenheit"
columns = ["size", "free", "used_pct", "temp", "smart"]

# Hot Files roots. Replaces the defaults ($HOME plus the OS log and tmp dirs).
watch_paths = ["~/src", "/var/log"]
# ...or keep the defaults and add to them.
extra_watch_paths = ["/srv/data"]

Precedence: CLI flag > environment variable > config file > built-in default. A flag you just typed always beats a file you wrote months ago.

Nothing in the file is fatal. An unknown key, an unparseable value, an unreadable file: each becomes a warning on stderr, the rest of the file still applies, and diskwatch still starts. --diag reprints the warnings, and the settings overlay says how many there were.

The format is a deliberate subset of TOML — flat key = value, # comments, no tables — so diskwatch parses it without taking on a dependency that would move its MSRV. Every file it accepts is also valid TOML.

Watch paths

The Hot Files tab watches $HOME plus the OS log and tmp directories by default. To point it somewhere else, in descending precedence:

diskwatch --watch ~/src --watch /var/log     # replace the defaults
diskwatch --watch-add /srv/data              # keep them, add one
DISKWATCH_WATCH_PATHS=/srv:/var/log diskwatch

Each root is watched recursively, which on Linux costs one inotify watch per directory underneath it. Point it at a large tree and you can exhaust fs.inotify.max_user_watches — diskwatch names the path and says so rather than reporting the kernel's bare "No space left on device", which sends people looking at df. A root that doesn't exist is named too, and doesn't stop the others being watched.

What's real, what's deferred

Metric macOS Linux
Device model / serial / firmware system_profiler + IOKit /sys/block/*/device/*
Per-device used bytes ✅ via APFS container map ✅ summed from mounts
Read/write byte rates, split ✅ IOKit Statistics /proc/diskstats 5/9
Read/write iops, split ✅ IOKit Operations /proc/diskstats 4/8
Avg per-op latency Total Time / Operations /proc/diskstats 6/10
p50 / p99 latency ✅ tick-averaged over 60s ✅ tick-averaged over 60s
Latency histogram, 7 buckets ✅ tick means weighted by ops ✅ tick means weighted by ops
True per-op p99 ❌ needs IOReport entitlement ❌ needs eBPF biolatency
Device utilisation (%util) ❌ IOKit has no busy-time counter /proc/diskstats 13
Requests in flight ❌ not exposed by IOKit /proc/diskstats 12
SMART attributes smartctl if installed smartctl if installed
Volumes — APFS / mdraid diskutil apfs list /proc/mdstat
Volumes — ZFS, LVM ⏳ deferred ⏳ deferred
Hot files — paths ✅ FSEvents ✅ inotify
Hot files — bytes / pid ❌ needs root or entitlement ❌ needs eBPF biosnoop
Capacity growth + time-to-full ✅ 10-min usage window ✅ 10-min usage window

Anything a platform can't measure renders -- and keeps its column, so the layout never shifts between machines. Two consequences worth knowing:

  • macOS reports no utilisation. IOKit counts service time, not time-with-IO-in-flight, which on a deep-queue NVMe exceeds wall clock. Dense sorts devices by throughput there and says so in its own border.
  • The latency histogram samples. Each 200 ms tick contributes its op count to the bucket holding that tick's mean service time. It shows a sustained slow stretch; it smears a lone 50 ms outlier into whatever its tick averaged.

Anti-goals

  • Not multi-host. Use NetWatch Cloud for a fleet view.
  • Not a daemon. No background collector, no persisted DB.
  • Not a cleaner. It surfaces what's eating disk; it deletes nothing.
  • Not a backup product. Snapshots are observed, not authored.
  • Not a benchmark. It measures what's happening, not what's possible.

License

MIT.

About

Single-host, read-only disk diagnostics TUI. Sibling to netwatch and syswatch.

Resources

Stars

235 stars

Watchers

5 watching

Forks

Releases

Packages

Contributors

Languages