A fast CLI/TUI tool for inspecting open ports and listening sockets on Linux.
Portly reads socket information from /proc/net/*, maps sockets to processes via /proc/<pid>/fd, and presents the result in an interactive terminal UI or plain text output (table, JSON, CSV).
- Interactive TUI with live refresh, search, and column sorting
- Plain CLI output for scripts and pipelines
- Filters by port, protocol, state, PID, process name, and address
- Deduplicated output (multiple sockets per interface collapsed into one row)
- JSON, CSV, and plain text export
- GNU-style long flags (
--port,--format, …) with short aliases (-p,-o, …)
- Linux (uses
/proc/net/tcp,/proc/net/udp, and related files) - Go 1.26+ (for building from source)
Note: Mapping sockets to PID/process name requires read access to
/proc/<pid>/fd. For ports owned by other users (especially root), run withsudo.
git clone https://github.com/<you>/portly.git
cd portly
make installEnsure your Go bin directory is on PATH:
go env GOPATH # e.g. /home/user/go
# binary is installed to: $(go env GOPATH)/bin/portlyfish shell:
fish_add_path (go env GOPATH)/binmake build
./portly --versionmake build GOOS=linux GOARCH=arm64portlyForce TUI or disable it:
portly --tui
portly --no-tui| Key | Action |
|---|---|
/ |
Search |
r |
Refresh |
s |
Cycle sort column (↑/↓ per column) |
S |
Reverse sort direction |
q |
Quit |
# Listening ports only (default)
portly --no-tui
# Filter by port
portly --no-tui -p 80
portly --no-tui --port 8000-9000
# Filter by process
portly --no-tui -n nginx
# All connection states
portly --no-tui --state all
# JSON output
portly --no-tui -o json
# CSV output
portly --no-tui --format csv -p 443
# Live refresh (non-interactive)
portly --no-tui -w --interval 1s
# Full process info for root-owned ports
sudo portly --no-tui -p 80| Format | Flag | Description |
|---|---|---|
table |
--format table (default) |
Aligned columns |
json |
-o json |
JSON array of connections |
csv |
-o csv |
CSV with header |
plain |
--format plain |
Tab-separated, no tabwriter |
Default columns: proto, local, remote, state, pid, process, user
portly --no-tui --columns proto,local,pid,process
portly --no-tui --no-header -o csv| Flag | Short | Description |
|---|---|---|
--tui |
-i |
Interactive TUI |
--no-tui |
Disable TUI | |
--watch |
-w |
Continuous refresh |
--interval |
Refresh interval (default 2s) |
|
--state |
listen, established, all, … (default listen) |
|
--port |
-p |
Port or range (8080, 8000-9000) |
--process |
-n |
Process name (substring) |
--pid |
Filter by PID | |
--protocol |
tcp, udp, tcp6, udp6, all |
|
--tcp / --udp |
Protocol shortcuts | |
--local / --remote |
Address filter | |
--ipv4 / --ipv6 |
Address family | |
--sort |
Sort field (see --help) |
|
--reverse |
-r |
Reverse sort order |
--format |
-o |
Output format |
--no-header |
Omit header row | |
--columns |
Comma-separated column list | |
--quiet |
-q |
Suppress hints (e.g. sudo warning) |
--version |
Print version |
Run portly --help for the full list.
make test # run tests
make build # production binary in ./portly
make clean # remove ./portly
make uninstall # remove from $GOBIN- Read socket entries from
/proc/net/tcp,udp,tcp6,udp6 - Parse hex addresses and TCP states
- Map socket inodes to PIDs by scanning
/proc/*/fd/* - Resolve process name and user from
/proc/<pid>/ - Fall back to socket UID from
/proc/net/*when PID mapping is unavailable
MIT — see LICENSE.