The Port Conflict Problem
Modern development stacks are complex. A typical project might run:
- Frontend dev server on port 3000
- API server on port 8080
- PostgreSQL on port 5432
- Redis on port 6379
- Storybook on port 6006
That is five ports for one project. If you work on multiple projects, or if any of these processes crash without releasing their port, you hit conflicts. You have all seen Error: listen EADDRINUSE.
Why Port Conflicts Keep Happening
Zombie processes
The most common cause. You Ctrl+C your dev server, but a child process (Webpack, esbuild, a database connection pool) keeps running in the background. The parent exited but the port is still held.
Crashed servers
Your server crashed during a hot reload. Node.js exits but the port stays in TIME_WAIT for 30-60 seconds. Or worse, a subprocess survived the crash and is still bound to the port.
Docker leftover containers
You ran docker-compose up yesterday. Today, those containers are still running and holding ports 5432, 6379, and 3000.
Multiple projects, same defaults
Every React app defaults to port 3000. Every Express app defaults to port 3000. You open two projects and immediately conflict.
Port Management Tools Compared
| Tool | Kill Port | Scan | Safety | Restart | Config | Cross-platform |
|---|---|---|---|---|---|---|
| lsof + kill | Manual | No | No | No | No | macOS/Linux |
| kill-port (npm) | Yes | No | No | No | No | Yes |
| fkill-cli | Yes | No | No | No | No | Yes |
| portrm | Yes | Yes | 3-tier | Yes | .ptrm.toml | Yes |
The Ideal Developer Workflow
Here is a port management setup that eliminates conflicts permanently:
Step 1: Install portrm globally
$ npm i -g portrm
# or: brew install abhishekayu/tap/portrm
Step 2: Add a .ptrm.toml to each project
# .ptrm.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
[profiles.staging]
frontend = { port = 3100 }
api = { port = 8180, env = { RUST_LOG = "info" } }
Step 3: Add pre-scripts to package.json
{
"scripts": {
"predev": "ptrm fix 3000 3001 -y",
"dev": "concurrently \"next dev\" \"node api/server.js\"",
"ports": "ptrm scan",
"ports:clean": "ptrm fix 3000 3001 5432 6379 -y"
}
}
Step 4: Use ptrm scan as a diagnostic
$ ptrm scan
# Shows all occupied ports with service names, PIDs, memory, safety levels
When something is not working, ptrm scan gives you immediate visibility into what is running.
Port Management in CI/CD
Port conflicts in CI are particularly painful because you cannot easily debug them. Common scenarios:
Parallel test runners
Jest, Vitest, and Playwright can run test suites in parallel. Each suite might spin up a test server. If they all try port 3000, tests fail randomly.
# In your CI script, clear ports before tests:
$ npx portrm fix 3000 4000 5000 -y
$ npm test
Leftover processes from previous builds
Self-hosted CI runners can have leftover processes from previous builds:
# CI cleanup step:
$ npx portrm fix 3000 8080 5432 -y || true
Team-Level Port Management
For teams working on microservices or monorepos with multiple services:
Standardize port assignments
Create a port registry in your project documentation:
| Service | Dev Port | Test Port |
|---|---|---|
| Frontend | 3000 | 4000 |
| API | 8080 | 9080 |
| Auth service | 8081 | 9081 |
| PostgreSQL | 5432 | 5433 |
| Redis | 6379 | 6380 |
Commit .ptrm.toml to your repo
This ensures every developer on the team has the same port configuration and safety rules. New team members can run ptrm up and get the full stack running without conflicts.
Use portrm in onboarding docs
# New developer setup:
$ git clone repo && cd repo
$ npm install
$ ptrm scan # Check for conflicts
$ ptrm fix 3000 -y # Clear if needed
$ npm run dev # Start development
Take control of your ports
One tool for scanning, fixing, and preventing port conflicts across your entire workflow.