<p align="center">
<h1 align="center">PORTCTL</h1>
<p align="center"><strong>Fix broken dev servers instantly.</strong></p>
<p align="center">A fast, cross-platform CLI that detects, explains, and fixes port conflicts, zombie processes, and broken dev servers - so you can get back to coding instantly.</p>
<p align="center">
<a href="https://crates.io/crates/portctl"><img src="https://img.shields.io/crates/v/portctl.svg" alt="crates.io"></a>
<a href="https://www.npmjs.com/package/portctl"><img src="https://img.shields.io/npm/v/portctl.svg" alt="npm"></a>
<a href="https://github.com/abhishekayu/portctl/releases"><img src="https://img.shields.io/github/v/release/abhishekayu/portctl" alt="release"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"></a>
</p>
</p>
<br>
```
$ portctl fix 3000
⚡ Port 3000 in use
→ Next.js (PID 81106) running for 7m 21s
→ 🛡 safe to kill
• Sending SIGTERM to PID 81106
• Verified: PID 81106 has exited
✔ Port 3000 is now free
Restart: npm run dev
```
<p align="center">
<b>Detects the process. Checks if it's safe. Kills it gracefully. Tells you how to restart.</b><br>
Try it now: <code>npx portctl scan</code>
</p>
---
## The Problem Every Developer Hits
You've seen this before:
```
Error: listen EADDRINUSE: address already in use :::3000
```
A crashed dev server. A zombie process. Something unknown squatting on your port.
So you do the ritual:
```bash
lsof -i :3000 # wall of text
kill -9 <pid> # hope it's not PostgreSQL
# ...was that important?
# how do I restart this thing?
```
This is fragile. It's blind. It tells you nothing about what you just killed.
**portctl replaces this entire workflow with one command.** A single CLI tool for port conflict resolution, process inspection, and dev server recovery.
---
## Instant Port Inspection
```
$ portctl 3000
⚡ Port 3000 in use
→ Next.js (PID 81106)
→ running for 7m 21s
→ memory 42.9 MB
→ detected Next.js (95% confidence)
→ 🛡 safe to kill
📂 Detected project: Next.js
→ dev command: npm run dev
→ default port: 3000
```
portctl tells you **what** is running, **whether it's safe** to kill, and **how to restart** it.
---
## Port Debugging: Before vs After
| Port 3000 stuck | `lsof -i :3000 \| awk ... \| xargs kill -9` | `portctl fix 3000` |
| "What's on my ports?" | `lsof -iTCP -sTCP:LISTEN` (unreadable) | `portctl scan` |
| "Is this safe to kill?" | Google the process name | portctl tells you |
| "How do I restart?" | Dig through package.json | portctl shows the command |
| Zombie processes | Hunt them one by one | `portctl doctor -y` |
| Which port is my frontend? | Check config files | `portctl group` |
---
## Install
### Try instantly (no install)
```bash
npx portctl scan
```
### Package managers
<table>
<tr><td><b>Homebrew</b></td><td>
```bash
brew tap abhishekayu/tap && brew install portctl
```
</td></tr>
<tr><td><b>Cargo</b></td><td>
```bash
cargo install portctl
```
</td></tr>
<tr><td><b>npm</b></td><td>
```bash
npm install -g portctl
```
</td></tr>
</table>
### Shell script (macOS / Linux)
```bash
### Build from source
```bash
git clone https://github.com/abhishekayu/portctl.git
cd portctl
cargo install --path .
```
> **Supports** macOS, Linux, and Windows. ~980KB binary. Zero runtime dependencies.
---
## Usage Examples
### Scan all listening ports
```
$ portctl scan
⚡ 5 active ports
PORT PROCESS PID SERVICE MEMORY UPTIME USER
────────────────────────────────────────────────────────────────────────────────
3000 node 81106 Next.js 42.9 MB 7m 17s abhishek
3898 Code Helper (Plugin) 34290 Python 20.2 MB 3h 12m abhishek
5237 Code Helper (Plugin) 34073 Python 20.1 MB 3h 12m abhishek
5932 Code Helper (Plugin) 61773 Python 57.5 MB 58m 35s abhishek
42050 OneDrive Sync Serv.. 36643 Unknown 14.4 MB 3d 1h abhishek
```
### Fix port conflicts and auto-restart
```
$ portctl fix 3000 --run "npm run dev"
✔ Killed safely port 3000 is now free
🚀 Restarting: npm run dev
```
One command. Port cleared, dev server restarted.
### Diagnose dev environment issues
```
$ portctl doctor
🩺 2 issues found
1. Idle process Code Helper (PID 34290) at 0.0% CPU [auto-fixable]
→ Idle Code Helper on port 3898 -- consider killing to free resources
2. Idle process Code Helper (PID 34073) at 0.0% CPU [auto-fixable]
→ Idle Code Helper on port 5237 -- consider killing to free resources
⚙ Run portctl doctor -y to auto-fix 2 issues
```
### Group ports by service role
```
$ portctl group --dev
⚡ 4 active ports in 2 groups
⚙ Frontend (2)
────────────────────────────────────────────────────────────────────────────
3000 node 81106 Next.js 42.9 MB 7m 21s abhishek
3898 Code Helper (Plugin) 34290 Python 20.2 MB 3h 12m abhishek
⚙ Backend (2)
────────────────────────────────────────────────────────────────────────────
5237 Code Helper (Plugin) 34073 Python 20.0 MB 3h 12m abhishek
5932 Code Helper (Plugin) 61773 Python 57.5 MB 58m 39s abhishek
```
### Interactive terminal UI
```
$ portctl ui
```
Arrow keys to navigate, enter to inspect, `f` to fix. Full interactive TUI for port management, powered by ratatui.
---
## CLI Commands Reference
| Command | Description | Example |
| -------------------------- | ----------------------------------------------------- | -------------------------------------- |
| `portctl scan` | List all listening ports with service, memory, uptime | `portctl scan` |
| `portctl <port>` | Inspect a single port in detail | `portctl 3000` |
| `portctl fix <port>` | Safely kill the process on a port | `portctl fix 3000` |
| `portctl fix <port> --run` | Kill and auto-restart a dev server | `portctl fix 3000 --run "npm run dev"` |
| `portctl fix <port> -y` | Skip confirmation prompt | `portctl fix 8080 -y` |
| `portctl kill <port>` | Direct kill with safety confirmation | `portctl kill 3000` |
| `portctl group` | Ports organized by role (frontend/backend/db/infra) | `portctl group --dev` |
| `portctl doctor` | Find stale servers, idle processes, conflicts | `portctl doctor` |
| `portctl doctor -y` | Auto-fix all safe issues | `portctl doctor -y` |
| `portctl history` | View past actions with timestamps | `portctl history` |
| `portctl history --stats` | Kill stats: success rate, top ports, top processes | `portctl history --stats` |
| `portctl project` | Detect project type, suggest dev commands | `portctl project` |
| `portctl ui` | Interactive TUI with keyboard navigation | `portctl ui` |
> All commands support `--json` for scripting and CI pipelines.
---
## Why portctl Over kill-port, fkill, or lsof
**It's not `kill -9` with extra steps.**
portctl is a process classification engine built for developer productivity:
- **Identifies services** -- Next.js, Vite, Django, Flask, Express, PostgreSQL, Redis, Docker, and 13+ categories with confidence scores
- **Safety system** -- blocks system-critical processes (PID 1, sshd, launchd), warns about databases (data loss risk), approves dev servers
- **Graceful shutdown** -- SIGTERM first, waits for clean exit, escalates to SIGKILL only if needed
- **Project-aware** -- reads package.json, Cargo.toml, pyproject.toml to suggest the right restart command
- **Docker-aware** -- detects container ports vs host ports
- **History** -- every action logged to `~/.portctl/history.json` with timestamps and outcomes
### How portctl works under the hood
1. **Scan** -- queries the OS for all listening ports, resolves PIDs via sysinfo
2. **Classify** -- identifies the service type (Next.js, PostgreSQL, Docker, etc.)
3. **Assess** -- safety check: SAFE / WARN / BLOCK
4. **Strategy** -- picks the right approach: Graceful, Escalating, or Force
5. **Execute** -- sends signals, waits for exit, verifies the port is free
6. **Recover** -- detects the project, suggests restart, or auto-restarts with `--run`
### Safety tiers
| Verdict | Examples | Behavior |
| ----------- | ----------------------------------------------------------- | ----------------------------------------------- |
| **BLOCKED** | PID 0/1, launchd, systemd, sshd, kernel_task | Refuses to kill |
| **WARNING** | PostgreSQL, MySQL, Redis, Docker, Nginx | Warns about consequences, asks for confirmation |
| **SAFE** | Next.js, Vite, Create React App, Django dev, Flask, Node.js | Kills gracefully |
---
## Developer Productivity Workflows
### "Port 3000 is already in use" after a crash
Your Next.js server crashed. The port is stuck. You just want to get back to coding.
```bash
portctl fix 3000 -y --run "npm run dev"
```
Port cleared, server restarted. One line.
### Make `npm run dev` crash-proof
Add portctl to your scripts so port conflicts resolve themselves:
```json
{
"scripts": {
"dev": "portctl fix 3000 -y --run 'next dev'",
"dev:api": "portctl fix 8080 -y --run 'node server.js'",
"dev:clean": "portctl doctor -y && npm run dev"
}
}
```
Now `npm run dev` works every time, even if something is already on port 3000.
### Morning dev environment reset
You open your laptop. Stale servers from yesterday are hogging ports and memory.
```bash
portctl doctor -y
```
Finds zombie processes, idle servers, and cleans them up automatically.
### "What is using port 8080?"
Something is squatting on your API port but you have no idea what.
```bash
portctl 8080
```
Shows the process name, PID, service type, memory, uptime, project directory, and whether it's safe to kill.
### Full-stack dev with multiple services
Frontend on 3000, API on 8080, database on 5432. You need a clear picture.
```bash
# See everything grouped by role
portctl group --dev
# Quick scan of all ports
portctl scan
```
### Shell aliases for daily use
```bash
# ~/.zshrc or ~/.bashrc
alias pf='portctl fix'
alias pfs='portctl scan'
alias pfd='portctl doctor -y'
alias dev3='portctl fix 3000 -y --run "npm run dev"'
alias dev8='portctl fix 8080 -y --run "node server.js"'
```
### Fix multiple ports at once
Clearing out a full dev environment before starting fresh:
```bash
portctl fix 3000 -y && portctl fix 8080 -y && portctl fix 5173 -y
```
### CI / pre-commit: ensure clean ports
```bash
# Fail if stale dev servers are running
### Pipe to scripts with JSON output
```bash
# Get all listening ports as JSON
# Count active dev servers
---
## Comparison: portctl vs kill-port vs fkill
| Service identification | No | Name only | Full (service, memory, uptime, CWD) |
| Safety checks | No | No | Yes (safe / warn / block) |
| Graceful shutdown | No | No | Yes (SIGTERM, then escalate) |
| Restart hints | No | No | Yes (project-aware) |
| Auto-restart | No | No | Yes (`--run`) |
| Docker awareness | No | No | Yes |
| Auto-diagnosis | No | No | Yes (`doctor`) |
| Port grouping | No | No | Yes (by role) |
| Action history | No | No | Yes |
| Interactive TUI | No | Yes | Yes |
| Platform | Node.js | Node.js | Native binary |
| Size | ~50MB | ~50MB | ~980KB |
---
## Architecture & Performance
```
src/
scanner/ Batch port scanning with sysinfo process resolution
classifier/ 13+ service classifiers with confidence scoring
engine/ Safety checks, strategy selection, graceful kill with retry
platform/ macOS (lsof + libc) / Linux (/proc/net/tcp) / Windows (netstat)
project/ Filesystem project detection (package.json, Cargo.toml, etc.)
docker/ Container awareness via docker ps
grouping/ Port role classification (frontend/backend/database/infra)
doctor/ Stale servers, idle processes, crowded ports
history/ Action log persisted to ~/.portctl/history.json
plugin/ Extensible ServiceDetector trait for custom detectors
cli/ Clap v4 + colored output + ratatui TUI
```
Built in Rust for speed and reliability. Ships as a single ~980KB static binary with zero runtime dependencies. No Node.js, no Python -- just a fast native CLI tool for managing ports and debugging dev environments.
---
## Contributing
```bash
git clone https://github.com/abhishekayu/portctl.git
cd portctl
cargo build
cargo test
```
- Report bugs or request features via [Issues](https://github.com/abhishekayu/portctl/issues)
- Add new service classifiers
- Improve platform support
- Write new doctor diagnostics
See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
---
## License
[MIT](LICENSE) -- free for personal and commercial use.
---
<p align="center">
<strong>portctl</strong> -- an open-source CLI tool for port management, process debugging, and developer environment recovery.<br>
Built for developers who are tired of <code>lsof</code> + <code>kill -9</code>.<br>
Works with Next.js, Vite, Django, Flask, Express, Docker, PostgreSQL, Redis, and more.
</p>