portop

htop, but for ports

portop

See which process, which systemd unit, which Docker container is using a port — and kill it, right from your keyboard.

Latest release CI License: MIT

Why portop

The everyday basics of seeing what's on your ports — done once, done well, with the context ss and lsof leave out.

Live, color-coded table

LISTEN, ESTABLISHED and transient states colored at a glance, with per-process CPU% sampled like top.

Kill, safely

k then confirm — graceful SIGTERM or forced SIGKILL. Never a stray keypress away.

{}systemd & Docker aware

Every row shows the owning .service unit and Docker container, resolved from cgroups — no extra daemons.

New-port alerts

Freshly opened listening ports are highlighted live, with an optional desktop notification.

±Baseline drift detection

--save-baseline then --diff from a cron job — exit code 3 the moment something unexpected starts listening.

Live settings screen

, opens it: cycle 12 themes (Dracula, Nord, Solarized, Gruvbox, Catppuccin, Tokyo Night, Monokai, Darcula, VS Code, Ubuntu, ...) with the arrow keys as the whole UI re-skins live, rebind any of 18 actions — saved automatically, no file to touch.

Beyond wrapping ss and lsof

Neither tells you this on its own — portop does, from the same dashboard.

Baseline drift detection--diff

Save a snapshot of what's listening, then diff against it later — from a cron job or systemd timer. Exit code 3 the instant a port you didn't expect shows up.

Who actually owns this portenter

systemd unit and Docker container, resolved straight from the process's cgroup — no D-Bus, no Docker SDK, no daemon of its own.

Honest about permissionsi

Can't resolve a row owned by root (docker-proxy, systemd-resolved)? portop tells you exactly why and that sudo would fix it, instead of a silent blank.

Adapts to your terminal80c

Columns drop themselves — least useful first — as the terminal narrows, so rows never wrap into a mess. Works down to 80 columns.

Well-known port names:22

Parsed straight from /etc/services: :22 shows as ssh, :443 as https, right in the table.

Scriptable--json

A clean JSON snapshot for piping into jq, a dashboard, or your own monitoring — same data the TUI shows, no scraping.

In action

portop
portop demo: filtering to a port, viewing process details, the kill confirmation, and the help overlay

Filtering to a port, checking who owns it, and the confirm-before-kill flow — a real recorded session, not staged.

Installation

Linux only — portop reads /proc/net directly, which doesn't exist on macOS or Windows.

# detects arch, verifies the release checksum
curl -fsSL https://raw.githubusercontent.com/padovanl/portop/main/install.sh | sh
# replace <version> with the latest release number, no leading "v"
curl -fLO https://github.com/padovanl/portop/releases/latest/download/portop_<version>_linux_amd64.deb
sudo dpkg -i portop_<version>_linux_amd64.deb
curl -fLO https://github.com/padovanl/portop/releases/latest/download/portop_<version>_linux_amd64.tar.gz
tar -xzf portop_<version>_linux_amd64.tar.gz
sudo install -m 755 portop /usr/local/bin/portop
go install github.com/padovanl/portop/cmd/portop@latest

arm64 builds and checksums on the releases page.

Every key, one dashboard

The complete keybinding reference — also in-app any time with ?, and fully remappable via config.yml.

KeyAction
/ Move the cursor
g / GJump to top / bottom
enterProcess details — cmdline, exe, cwd, user, RSS, start time
kKill process, then y=SIGTERM or f=SIGKILL
oOpen http(s)://localhost:PORT in the browser
f / /Filter/search by port, process or PID
vToggle IPv4 / IPv6 / both
eShow/hide ESTABLISHED connections
sCycle sort column
cCopy selected row to clipboard
nClear the new-port highlight
rRefresh now
,Settings — live theme picker, rebind any key
?Help
qQuit