DEV Community

Cover image for I replaced lsof, ss, and netstat with a single Rust binary
Mark Marosi
Mark Marosi

Posted on

I replaced lsof, ss, and netstat with a single Rust binary

The problem

Every developer has been here: something is hogging port 3000 and you need to find out what.

On Linux you try ss -tlnp | grep 3000. On macOS it's lsof -i :3000. On Windows... good luck. Each gives different output, different flags, and none of them tell you how long the process has been running, how much memory it's eating, or whether it's a Docker container.

I got tired of this. So I built portview.

One command, everything you need

$ portview
Enter fullscreen mode Exit fullscreen mode

That's it. Every listening port, the process behind it, PID, user, uptime, memory usage, and the full command -- in a colored table. Cross-platform. ~1.3 MB single binary. Zero runtime dependencies.

PORT  PROTO  PID    USER   PROCESS   UPTIME   MEM     COMMAND
3000  TCP    48291  mark   node      3h 12m   248 MB  next dev
5432  TCP    1203   pg     postgres  14d 2h   38 MB   /usr/lib/postgresql/16/bin/postgres
6379  TCP    1198   redis  redis     14d 2h   12 MB   redis-server *:6379
8080  TCP    51002  mark   python3   22m      45 MB   uvicorn main:app --port 8080
Enter fullscreen mode Exit fullscreen mode

No parsing lsof output through awk. No remembering whether it's -tlnp or -tulpn. Just portview.

But I didn't stop there

A port viewer that just lists ports isn't worth writing about. Here's what makes portview different:

Interactive TUI with tree view

portview watch opens a live-refreshing TUI. Navigate with j/k, press Enter to inspect a port in detail (full command, working directory, child processes, open connections), press d to kill it.

Press t to toggle tree view -- it groups child processes under their parents so you can see which workers belong to which master process:

PROCESS
node
├── node (worker)
├── node (worker)
└── node (worker)
postgres
└── postgres: autovacuum
Enter fullscreen mode Exit fullscreen mode

Docker as a first-class citizen

portview watch --docker shows Docker containers as rows in the table. No more running docker ps in a separate terminal and cross-referencing ports. Press d on a container row to Stop, Restart, or tail Logs.

portview doctor

This is my favorite feature. portview doctor runs five diagnostic checks on your ports:

$ portview doctor
  ✓ No port conflicts
  ✗ postgres is listening on 0.0.0.0:5432 -- consider binding to 127.0.0.1
  ✓ No Docker-host conflicts
  ✓ No stale connections
  ✓ No high-resource listeners
Enter fullscreen mode Exit fullscreen mode

It catches: port conflicts (two processes fighting over the same port), databases exposed on all interfaces, Docker port collisions with host processes, TIME_WAIT/CLOSE_WAIT pileups, and memory hogs. Use portview doctor --json with exit code 1 on errors for CI.

SSH remote mode

portview ssh user@server          # scan remote ports
portview ssh user@server watch    # full remote TUI
portview ssh user@server doctor   # remote diagnostics
Enter fullscreen mode Exit fullscreen mode

It shells out to your system ssh binary (inherits your config, keys, ProxyJump, everything), runs portview --json on the remote host, and renders the output locally. Kill actions in the remote TUI are forwarded over SSH. No agents, no daemons, no new ports to open.

How it works

No shelling out to lsof or ss. portview reads directly from the OS:

  • Linux: Parses /proc/net/tcp, maps inodes to PIDs via /proc/*/fd/, reads process metadata from /proc/<pid>/status and /proc/<pid>/stat
  • macOS: Uses libproc FFI (proc_listpids, proc_pidfdinfo, proc_pidinfo)
  • Windows: Uses iphlpapi (GetExtendedTcpTable) and kernel32 (CreateToolhelp32Snapshot, GetProcessTimes)

This is why it's fast -- there's no subprocess overhead.

Install

curl -fsSL https://raw.githubusercontent.com/mapika/portview/main/install.sh | sh   # Linux/macOS
brew install mapika/tap/portview                                                      # Homebrew
cargo install portview                                                                # Cargo
Enter fullscreen mode Exit fullscreen mode

Or grab a binary from the releases page.

What's next

The codebase is ~6K lines of Rust. MIT licensed. Contributions welcome.

If you've ever been frustrated by lsof, give it a try and let me know what you think: github.com/Mapika/portview

Top comments (0)