portls
Modern cross-platform port inspector in Rust. Clean replacement for ss, netstat, and lsof.
Installation
Or build from source:
This installs the ports command.
Usage
List all listening ports
┌──────┬───────┬───────┬─────────────┬─────────┬─────────────┐
│ PORT │ PROTO │ PID │ PROCESS │ SERVICE │ ADDRESS │
├──────┼───────┼───────┼─────────────┼─────────┼─────────────┤
│ 22 │ tcp │ 1001 │ sshd │ ssh │ 0.0.0.0:22 │
│ 80 │ tcp │ 1234 │ nginx │ http │ 0.0.0.0:80 │
│ 443 │ tcp │ 1234 │ nginx │ https │ 0.0.0.0:443 │
│ 3000 │ tcp │ 5678 │ node │ - │ 127.0.0.1 │
│ 5432 │ tcp │ 9012 │ postgres │ postgres│ 127.0.0.1 │
└──────┴───────┴───────┴─────────────┴─────────┴─────────────┘
5 result(s)
Well-known ports automatically show a SERVICE name (ssh, http, https, postgres, redis, etc.).
Query by port or process name
Regex filtering
Show established connections
┌──────┬───────┬──────┬──────────┬─────────────────┬──────────────────┐
│ PORT │ PROTO │ PID │ PROCESS │ LOCAL │ REMOTE │
├──────┼───────┼──────┼──────────┼─────────────────┼──────────────────┤
│ 443 │ tcp │ 1234 │ curl │ 192.168.1.5:443 │ 93.184.216.34:80 │
└──────┴───────┴──────┴──────────┴─────────────────┴──────────────────┘
Watch mode with live updates
New entries are highlighted in green.
Kill processes
Interactive mode
Use ↑/↓ or j/k to navigate, Enter to select, q to quit.
Real-time TUI (htop for ports)
Controls:
Tab— Toggle between listening/connections modep/i/n— Sort by port/pid/name↑/↓/j/K— NavigatePgUp/PgDn— Page navigationk— Kill selected process (shows confirmation popup)q— Quit
New ports are highlighted green for 3 seconds.
Port usage history
Track port usage over time with SQLite-backed history:
Example diff output:
┌──────┬───────┬──────────┬─────────────┐
│ PORT │ PROTO │ PROCESS │ ACTION │
├──────┼───────┼──────────┼─────────────┤
│ 3000 │ tcp │ node │ appeared │
│ 8080 │ tcp │ python │ disappeared │
└──────┴───────┴──────────┴─────────────┘
Example cron job for continuous monitoring:
# Record port state every 5 minutes
History data is stored in ~/.local/share/ports/ports_history.db.
Docker container awareness
When ports are forwarded by Docker, ports automatically shows which container they map to (via the Docker API — no subprocess overhead):
┌──────┬───────┬──────┬──────────────┬───────────────┬─────────┬──────────────┐
│ PORT │ PROTO │ PID │ PROCESS │ CONTAINER │ SERVICE │ ADDRESS │
├──────┼───────┼──────┼──────────────┼───────────────┼─────────┼──────────────┤
│ 80 │ tcp │ 1234 │ docker-proxy │ nginx-prod │ http │ 0.0.0.0:80 │
│ 443 │ tcp │ 1234 │ docker-proxy │ nginx-prod │ https │ 0.0.0.0:443 │
│ 5432 │ tcp │ 5678 │ docker-proxy │ postgres-db │ postgres│ 0.0.0.0:5432 │
│ 3000 │ tcp │ 9012 │ node │ - │ - │ 127.0.0.1 │
└──────┴───────┴──────┴──────────────┴───────────────┴─────────┴──────────────┘
You can also query by container name:
Filter and sort
JSON output
Shell Completions
# Generate and save
# Or eval dynamically in shell config
| # ~/.config/fish/config.fish
Claude Code Integration
This repo ships with a Claude Code skill at .claude/commands/ports.md. When you open this project in Claude Code, it automatically knows the full ports CLI — every subcommand, flag, and common recipe. No MCP server to install, no config to set up.
Ask it to kill a port, monitor connections, check history diffs, or anything else ports can do and it'll use the CLI directly through bash.
Platform Support
| Platform | Listening ports | Connections |
|---|---|---|
| Linux | Native /proc/net parsing |
Native /proc/net |
| macOS | listeners crate |
lsof |
| Others | listeners crate fallback |
— |
Examples
# Find what's blocking port 8080
# Monitor all network activity in real-time
# Kill all Node.js processes on any port
# Kill a process that only has established connections (no listening socket)
# Export all listening ports as JSON
# Watch PostgreSQL connections
# Find all web servers with regex
# Interactively select and kill a port
# See what changed since the last snapshot
&&