portctl 0.3.0

Fix port conflicts, debug processes, and recover dev environments instantly
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
<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

| Task                       | Before                                      | 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`           |
| Start entire dev stack     | Open 5 terminals, run commands manually     | `portctl up`              |
| Stop everything            | Find and kill each process                  | `portctl down`            |
| "Is my port free?"         | `lsof -i :3000` and parse output            | `portctl preflight 3000`  |
| Monitor a flaky server     | Watch logs + manual restart                 | `portctl watch 3000`      |

---

## 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
curl -fsSL https://raw.githubusercontent.com/abhishekayu/portctl/main/install.sh | sh
```

### 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.

### Define your dev stack with `.portctl.toml`

```bash
$ portctl init

  ✔ Created .portctl.toml
  → Edit the file to define your services.
```

Edit the generated config to declare your services:

```toml
[project]
name = "my-app"

[services.frontend]
port = 3000
run = "npm run dev"
cwd = "./frontend"
preflight = true

[services.api]
port = 8080
run = "cargo run"
cwd = "./backend"
preflight = true

[services.worker]
port = 9090
run = "python worker.py"
env = { PYTHON_ENV = "development" }
```

### Start your entire dev stack

```
$ portctl up

  🚀 Starting 3 services...

  ✔ api started on port 8080
  ✔ frontend started on port 3000
  ✔ worker started on port 9090

  ✔ 3/3 services started.
```

Pre-flight checks run automatically. If a port is busy, portctl tells you. Add `-y` to auto-fix conflicts before starting.

### Stop everything

```
$ portctl down

  🛑 Stopping 3 services...

  ✔ api stopped (port 8080)
  ✔ frontend stopped (port 3000)
  ✔ worker stopped (port 9090)

  ✔ 3/3 services stopped.
```

### Pre-flight port check

```
$ portctl preflight 3000 8080 5432

  🔍 Pre-flight check for 3 ports...

  ✔ Port 3000 is free
  ✔ Port 8080 is free
  ✘ Port 5432 is busy -- postgres (PID 1234, PostgreSQL)

  ⚠ 1 port is already in use. Run portctl fix <port> to fix.
```

Run without arguments to check all ports from `.portctl.toml`.

### Watch a port and auto-restart on crash

```
$ portctl watch 3000

  👀 Watching port 3000 (every 2s, Ctrl-C to stop)

  ✔ Port 3000 is up -- Next.js (PID 81106)
  ✘ Port 3000 went down -- Unknown crash reason (was PID 81106)
  🚀 Auto-restarting: npm run dev
  ✔ Port 3000 recovered (PID 82001, Next.js) -- downtime: 3s
```

If a `.portctl.toml` defines a `run` command for the watched port, portctl auto-restarts it when it crashes.

---

## 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`                           |
| `portctl init`             | Create a `.portctl.toml` config in current directory  | `portctl init`                         |
| `portctl up`               | Start all services from `.portctl.toml`               | `portctl up`                           |
| `portctl up -y`            | Start services, auto-fix port conflicts first         | `portctl up -y`                        |
| `portctl down`             | Stop all services from `.portctl.toml`                | `portctl down`                         |
| `portctl preflight`        | Check if ports are free before starting               | `portctl preflight 3000 8080`          |
| `portctl watch <port>`     | Monitor a port, alert on crash, auto-restart          | `portctl watch 3000`                   |

> 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 `.portctl.toml`

Frontend on 3000, API on 8080, worker on 9090. Define them once, manage them forever:

```bash
# Initialize config
portctl init

# Start everything (runs preflight checks automatically)
portctl up

# Stop everything at end of day
portctl down
```

No more opening 5 terminals and running commands manually.

### Monitor a flaky dev server

Your dev server keeps crashing. Let portctl watch it and auto-restart:

```bash
portctl watch 3000
```

If a `.portctl.toml` defines a `run` command for port 3000, portctl auto-restarts it when it goes down.

### Pre-flight check before deployment scripts

```bash
# Check all ports from .portctl.toml are free
portctl preflight

# Check specific ports
portctl preflight 3000 8080 5432
```

### Shell aliases for daily use

```bash
# ~/.zshrc or ~/.bashrc
alias pf='portctl fix'
alias pfs='portctl scan'
alias pfd='portctl doctor -y'
alias pu='portctl up'
alias pd='portctl down'
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
# The old way
portctl fix 3000 -y && portctl fix 8080 -y && portctl fix 5173 -y

# The new way -- with .portctl.toml
portctl down && portctl up
```

### CI / pre-commit: ensure clean ports

```bash
# Fail if stale dev servers are running
portctl doctor --json | jq -e 'length == 0' || { portctl doctor -y; }
```

### Pipe to scripts with JSON output

```bash
# Get all listening ports as JSON
portctl scan --json | jq '.[] | select(.service == "Next.js")'

# Count active dev servers
portctl scan --json | jq '[.[] | select(.service != "Unknown")] | length'
```

---

## Comparison: portctl vs kill-port vs fkill

|                        | kill-port | fkill     | **portctl**                         |
| ---------------------- | --------- | --------- | ----------------------------------- |
| 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                                 |
| Project config file    | No        | No        | Yes (`.portctl.toml`)               |
| Dev stack up/down      | No        | No        | Yes (`up` / `down`)                 |
| Port monitoring        | No        | No        | Yes (`watch`)                       |
| Pre-flight checks      | No        | No        | Yes (`preflight`)                   |
| Crash detection        | No        | No        | Yes (signal, OOM, zombie)           |
| Platform               | Node.js   | Node.js   | Native binary                       |
| Size                   | ~50MB     | ~50MB     | ~1.2MB                              |

---

## 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
  config/       .portctl.toml project config loader
  watch/        Continuous port monitoring with crash detection
  stack/        Dev stack orchestration (up/down)
  preflight/    Pre-flight port availability checks
  crash/        Crash reason detection (signal, OOM, zombie)
  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>
  Define your dev stack in <code>.portctl.toml</code>, start with <code>portctl up</code>, stop with <code>portctl down</code>.<br>
  Works with Next.js, Vite, Django, Flask, Express, Docker, PostgreSQL, Redis, and more.
</p>