witr: Process Tracing CLI for Linux, macOS, Windows
witr answers a question ps, lsof, and docker ps can't: why is this process actually running, and what put it there. Reach for it when you're chasing a mystery port or a container that won't die and want one causal chain instead of stitching together four tools by hand. Skip it if you already know exactly what started your one well-understood service.
The Challenge of System Causality
The gap witr closes is that ps, top, lsof, ss, systemctl, and docker ps each show state, not causation — you get a PID or a listening socket, but you're left manually cross-referencing systemd units, parent shells, tmux sessions, and container runtimes to figure out who actually started it and why it's still running. witr treats that as a single question and builds the causal chain (supervisor, container, shell, or session) in one command instead of five.
What witr Does
witr is a command-line and terminal UI tool written in Go that traces a running process, open port, container, or held file back to the exact chain of systems that started it. It works from process, port, file, or container queries, or via an interactive TUI with four tabs covering processes, ports, containers, and file locks, and it can print the result as human-readable text or machine-readable JSON.
Key Capabilities and Modes
- ✓A single command answers 'why is this running' by building a causal ancestry chain — the README calls this the tool's core value — instead of state dumps from separate tools.
- ✓Four query modes that are repeatable and mixable in one invocation: --pid, --port, --file, and --container, plus positional process-name search (substring by default, --exact for strict matching).
- ✓A four-tab interactive TUI (Processes, Ports, Containers, Locks) with a live side panel, mouse support, sortable/filterable columns, and an adaptive light/dark theme.
- ✓Container awareness across eight runtimes in one query: Docker, Podman, nerdctl, K8s/crictl, Incus, LXC, LXD, and FreeBSD jails, matched by name, image, command, or compose project/service label.
- ✓Process actions from inside the TUI — send Kill, Terminate, Pause, or Resume signals, or renice a process — on Unix systems.
- ✓Machine-readable --json output plus six meaningful exit codes (0-5) built for scripts, CI pipelines, and monitoring checks rather than just terminal reading.
- ✓Best-effort source attribution that picks one primary cause per process — a systemd unit with schedule info, an SSH session with remote IP, a docker container, pm2, cron, or a detected tmux/screen session — instead of listing every contributing factor.
- ✓Auto-refreshing TUI panels on an adaptive cadence that starts at 3 seconds and backs off under load.
When to Use witr
- •Tracking down what's bound to a port before you can start your own service, e.g. witr --port 5000 --short to get the ancestry in one line.
- •Root-causing a container that keeps restarting or won't stop, by querying it by name, image, or compose project across Docker/Podman/K8s runtimes.
- •Incident response, where you need the causal chain — not just a PID — to explain to a team why a process exists and what's supervising it.
- •Finding what's holding a file lock, like the README's dpkg lock example (witr --file /var/lib/dpkg/lock).
- •Live system exploration via the TUI when you want to browse processes, ports, containers, and locks together instead of running four separate commands.
- •Scripting health checks or CI gates around witr's exit codes and --json output instead of parsing free-text ps/lsof output.
Getting Started with witr
witr ships as a single static binary for Linux, macOS, FreeBSD, and Windows, with no runtime dependencies to install separately. The quickest path is the install script — curl -fsSL https://raw.githubusercontent.com/pranshuparmar/witr/main/install.sh | bash on Unix, or irm .../install.ps1 | iex in PowerShell on Windows — but the README also lists package-manager installs for apt (Ubuntu 26.04+ and Debian sid and later), Homebrew, MacPorts, Conda/mamba/pixi, the AUR (witr-bin), winget, npm (@pranshuparmar/witr), FreeBSD Ports, Chocolatey, Scoop, AOSC OS, GNU Guix, Uniget, Aqua, Brioche, and Mise, plus prebuilt .deb/.rpm/.apk packages and go install github.com/pranshuparmar/witr/cmd/witr@latest for source installs. The README notes that community-packaged versions (apt, and packages tracked on Repology generally) can lag the latest GitHub release.
Using witr: Commands and Outputs
Run witr with no arguments, or with -i / --interactive, to open the TUI's four tabs (Processes, Ports, Containers, Locks). For a one-shot answer, pass a process name (witr node), or a target flag: --pid, --port, --file, or --container — all four are repeatable and can be combined in one call. --short prints only the ancestry line, --tree renders it as a tree (capped at 10 child processes), and --json returns machine-readable output for scripts. --exact turns off the default substring/fuzzy name matching. When a query is ambiguous, witr lists every match with its PID and re-run instructions rather than guessing which one you meant.
Strengths
- ✓One static binary, four platforms (Linux, macOS, FreeBSD, Windows), zero separate runtime dependencies to install.
- ✓Ships in apt, Homebrew, MacPorts, Conda, AUR, winget, npm, FreeBSD Ports, Chocolatey, Scoop, and more, per the README's own list.
- ✓The TUI genuinely merges four separate views (processes, ports, containers, locks) with mouse support and an ancestry side panel, instead of being a thin wrapper around one of them.
- ✓Container support spans eight runtimes in a single --container query, from Docker and Podman to K8s/crictl and FreeBSD jails.
- ✓Six distinct exit codes plus --json output make it usable as a scripted check, not just an interactive tool.
Understanding witr's Output and Edge Cases
- △Process actions from the TUI — Kill, Terminate, Pause, Resume, and Renice — are Unix-only, per the README, so Windows users get tracing but not in-TUI process control.
- △Source attribution is deliberately narrowed to one primary cause per process; a process with several plausible supervisors (say, both a container runtime and a shell) only gets the one witr judged primary.
- △Tree output caps child processes at 10, so a process with a very wide fan-out won't show its full family in --tree mode.
- △Full detail sometimes needs superuser privileges — the README defines exit code 3 specifically for permission-denied cases, so some queries against other users' processes come back incomplete without sudo.
- △Package-manager installs can lag the GitHub release, by the README's own note — apt, and packages tracked on Repology generally, may not have the newest features until you use the install script instead.
Other System Observability Tools
Who Benefits from witr?
witr is worth installing if you're a developer, sysadmin, or on-call engineer who regularly asks 'what started this' about a process, port, or container — especially if you work across Docker/Podman/Kubernetes and want one tool instead of stitching together ps, lsof, and docker ps output by hand. Skip it if your systems are simple enough that you already know every process's origin, or if you need deep protocol-level packet inspection — witr explains ancestry and context, not network traffic.
Common Questions about witr
witr runs as a single static binary on Linux, macOS, FreeBSD, and Windows — the README ships separate install scripts (install.sh for Unix, install.ps1 for PowerShell) and per-platform package-manager options for each.
witr's TUI opens when you run witr with no arguments or with -i / --interactive. It has four tabs — Processes, Ports, Containers, and Locks — each with a live side panel, sortable columns, mouse support, and an adaptive light/dark theme.
witr can trace a process by name or PID, an open port, a file that's held open (like a lock file), or a container across eight supported runtimes, then builds the causal chain — supervisor, container, shell, or session — that explains why it exists.
witr is open source, released under the Apache-2.0 license per the repository's LICENSE file, with its source hosted publicly on GitHub.
witr's six exit codes (0 through 5) are built for scripts, CI pipelines, and monitoring checks: 0 means clean, 1 means warnings, 2 means not found, 3 means permission denied, 4 means invalid input, and 5 means an internal error.
witr's --container flag traces processes inside containers across Docker, Podman, nerdctl, K8s/crictl, Incus, LXC, LXD, and FreeBSD jails, matching by container name, image, command, or compose project/service label.
Related repositories
Still deciding about witr?
One click hands the question to an AI along with this page — see what it says about witr.
