All Posts
RustCLIDeveloper ToolsNetworking
September 10, 2026

Building kport: A Zero-Dependency Port Killer in Rust

Why I got tired of lsof and kill -9, and how I built a fast Rust port killer published on both Crates.io and NPM.

The Problem: EADDRINUSE

Every developer knows this error:

Error: listen EADDRINUSE: address already in use :::3000

It happens when a dev server crashes without closing its sockets or when a node/bun process keeps running in the background.

The default fix is typing two separate commands:

lsof -i :3000
kill -9 <PID>

Or combining them into a bash snippet:

kill -9 $(lsof -t -i:3000)

This is tedious. On Windows, you have to use netstat -ano | findstr :3000 followed by taskkill. Spawning lsof or netstat sub-processes also takes time and behaves differently on different operating systems.

I built kport to do one thing quickly: inspect socket tables directly and kill whatever is holding the port.


Direct Kernel Queries

Instead of calling external shell commands, kport reads socket bindings straight from the OS kernel:

  • Linux: Reads /proc/net/tcp, /proc/net/tcp6, and /proc/[pid]/fd.
  • macOS: Uses the sysctl interface (NET_RT_STAT) to resolve socket inodes to PIDs.
  • Windows: Calls GetExtendedTcpTable via iphlpapi.dll.

Querying socket state directly takes less than 2 milliseconds.

pub fn find_process_on_port(port: u16) -> Result<Option<ProcessInfo>, KportError> {
    #[cfg(target_os = "linux")]
    return linux::get_port_owner(port);

    #[cfg(target_os = "macos")]
    return macos::get_port_owner(port);

    #[cfg(target_os = "windows")]
    return windows::get_port_owner(port);
}

Dual Publishing: Cargo + NPM

I wanted kport to be accessible whether someone works in Rust or JavaScript.

Rust via Cargo

cargo install kport

JS/TS via Bun or NPX (Zero Install)

Node developers don't always have Cargo installed, but they do have npm or bun.

Publishing @neuralshyam/kport on NPM allows zero-install execution:

bunx @neuralshyam/kport 3000
npx @neuralshyam/kport 3000

The NPM package isn't written in JavaScript with Node overhead. It contains a tiny JS wrapper script. On postinstall (or first run), it downloads the matching native binary compiled for darwin-arm64, linux-x64, or win32-x64.


Interactive TUI Mode

Running kport without arguments opens a terminal dashboard:

kport
┌────────────────────────────────────────────────────────────────────────┐
│ kport v0.2.2 - Active Ports                                           │
├────────┬────────┬──────────────────────┬─────────────┬─────────────────┤
│ PORT   │ PID    │ PROCESS              │ MEMORY      │ STATUS          │
├────────┼────────┼──────────────────────┼─────────────┼─────────────────┤
│ 3000   │ 41829  │ node (next-dev)      │ 142 MB      │ LISTENING       │
│ 5432   │ 1102   │ postgres             │ 38 MB       │ LISTENING       │
│ 6379   │ 1250   │ redis-server         │ 12 MB       │ LISTENING       │
│ 8080   │ 88210  │ bun (api)            │ 24 MB       │ LISTENING       │
└────────┴────────┴──────────────────────┴─────────────┴─────────────────┘
 [Up/Down] Select  [Space] Mark  [K] Kill  [F] Force Kill  [Q] Quit

The TUI uses crossterm and ratatui for fast rendering and keyhandling.


Other Features

  • Port ranges & wildcards: kport 5000..5010 "80*"
  • HTTP/TCP probing: kport --probe 3000
  • Orphan process sweep: kport --zombies

Links

Written by

Shyam