ncda is an ncdu-like terminal monitor for live Linux file I/O. It uses eBPF to show system-wide reads and writes by path and process, either in an interactive TUI or as periodic text output.
The demo uses real eBPF events. It can be reproduced with docs/demo.tape and scripts/demo-workload.sh.
brew install angristan/tap/ncdaThe formula installs both ncda and ncda-bench on Linux x86_64 and ARM64.
Run the source-built package without installing it:
nix run github:angristan/ncda -- --versionInstall it into a user profile:
nix profile install github:angristan/ncdaFor a declarative NixOS configuration, add ncda as a flake input:
inputs.ncda.url = "github:angristan/ncda";Then include its package where inputs is available:
environment.systemPackages = [
inputs.ncda.packages.${pkgs.system}.default
];The flake builds the eBPF object and the ncda and ncda-bench userspace
binaries from the committed Rust source and Cargo.lock. It supports native
Linux ARM64 and x86_64 builds. The consumer's lock file pins ncda's tested
Nixpkgs and rust-overlay revisions. Advanced configurations can make its
Nixpkgs input follow the host after verifying that LLVM 22 and the pinned Rust
toolchains remain available.
Release archives, SHA-256 checksums, and build provenance are available on the releases page.
sudo ncda # interactive TUI
sudo ncda --stdout # periodic text summary
sudo ncda --rate-window 10 # 10-second rolling rates
sudo ncda --exclude /var/cache # repeatable path exclusionPress ? in the TUI for keybindings and filter syntax. By default, /proc, /sys, and /dev are excluded.
ncda reports successful reads and writes that transferred data. Failed calls, zero-byte calls, attribution failures, and capture loss remain visible as diagnostics. Ambiguous paths are placed under /[unresolved]/ rather than assigned to a plausible but incorrect file.
See Capture model for syscall coverage, path attribution, and loss semantics.
- Linux 6.1 or newer
- Native x86_64 or AArch64 userspace
- Root, or
CAP_BPF+CAP_PERFMON(CAP_SYS_PTRACEmay be needed for path resolution)
The syscall decoder intentionally ignores x86 ia32/x32 and ARM AArch32 compatibility calls. Startup errors include the detected capabilities, kernel lockdown state, and memory-lock limits when eBPF initialization fails.
rustup toolchain install 1.97.1
rustup toolchain install nightly-2026-08-04 --profile minimal --component rust-src
cargo install bpf-linker --version 0.11
cargo build --release --locked --binsThe eBPF nightly is pinned because its LLVM version must match bpf-linker 0.11.
The default Nix development shell provides stable Rust, the pinned eBPF nightly, and the matching linker. Normal Cargo builds remain source-aware:
nix develop
cargo build --release --locked --binsA dedicated shell is also available for building only the eBPF object:
nix develop .#ebpf
cargo build --locked --release --package ncda-ebpf --bin ncda \
--target bpfel-unknown-none -Z build-std=corecargo fmt --all -- --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked
cargo build --release --locked --bins
nix flake check --no-update-lock-file
scripts/test-ebpf.shThe normal checks are unprivileged. scripts/test-ebpf.sh builds as the current user and runs only the integration test executable with sudo. Nix checks build and smoke-test both binaries, verify formatting, and assert the pinned Rust, LLVM, and linker versions. They do not run privileged live eBPF tests.
For reproducible capture measurements, see Benchmarking.
MIT.