Scripting
--wait and --free handle the two port chores of development and CI: waiting for a service, and getting a port back.
Wait for a port
portop --wait 5432 --timeout 1m && ./migrate
Exits 0 as soon as something listens on the port, or 1 when the timeout (default 30s) passes. Add /udp for a UDP port: --wait 53/udp.
Free a port
portop --free 3000 # SIGTERM, then wait up to 5s for the port portop --free 3000 --force # SIGKILL to every process holding it portop --free 53/udp --yes # no confirmation
--free lists what it will stop and, on a terminal, asks for confirmation. It records each process's start time before asking and refuses to signal one that changed in the meantime. It exits 0 when the port is free and 1 when it is still in use after --timeout.
SIGTERM goes to a server's master process, which stops its workers. --force signals the workers too, because SIGKILL to a master leaves them listening. A port held by a process portop cannot see is reported with a hint to run it with sudo.
JSON recipes
# ports reachable from the network
portop --json --listen | jq -r '.[] | select(.scope != "loopback") | "\(.local_port)/\(.protocol) \(.process // "?")"'
# who listens on 8080
portop --json --listen 8080 | jq '.[0] | {pid, process, user, container}'