| name | nodejs-port-cleanup |
| description | | Use when this capability is needed. |
Node.js Port Cleanup on Server Start
Problem
When restarting a development server, you often encounter EADDRINUSE: address already in use
errors because a previous instance is still running. This is especially common when:
- The server was started in a background process that wasn't properly terminated
- The terminal was closed without stopping the server
- The server crashed but the process didn't exit cleanly
Context / Trigger Conditions
- Error:
Error: listen EADDRINUSE: address already in use :::PORT
- Attempting to start a dev server that was recently running
- Background tasks holding ports after their parent process ended
- IDE or terminal sessions that didn't clean up properly
Solution
Add this function to your Node.js server startup script:
import { execSync } from 'child_process';
function killProcessOnPort(port: number): void {
const isWindows = process.platform === 'win32';
try {
if (isWindows) {
const netstatOutput = execSync(`netstat -ano | findstr :${port}`, {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
});
const lines = netstatOutput.trim().split('\n');
const pids = new Set<string>();
for (const line of lines) {
if (line.includes('LISTENING')) {
const parts = line.trim().split(/\s+/);
const pid = parts[parts.length - 1];
if (pid && /^\d+$/.test(pid)) {
pids.add(pid);
}
}
}
for (const pid of pids) {
try {
execSync(`taskkill /PID ${pid} /F`, {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
});
console.log(`Killed existing process on port ${port} (PID: ${pid})`);
} catch {
}
}
} else {
const lsofOutput = execSync(`lsof -ti:${port}`, {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
});
const pids = lsofOutput.trim().split('\n').filter(Boolean);
for (const pid of pids) {
try {
execSync(`kill -9 ${pid}`, { stdio: ['pipe', 'pipe', 'pipe'] });
console.log(`Killed existing process on port ${port} (PID: ${pid})`);
} catch {
}
}
}
} catch {
}
}
const PORT = 3000;
killProcessOnPort(PORT);
const server = http.createServer();
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
Verification
When the function successfully kills a process, you'll see:
Killed existing process on port 3847 (PID: 12345)
If no process was found (port was free), the function silently proceeds.
Example
Before this fix:
> npx tsx scripts/my-server.ts
Error: listen EADDRINUSE: address already in use :::3847
After adding killProcessOnPort():
> npx tsx scripts/my-server.ts
Killed existing process on port 3847 (PID: 39592)
Server running at http://localhost:3847
Notes
- Windows: Uses
netstat -ano to find PIDs and taskkill /F to force-kill
- Unix/Mac: Uses
lsof -ti:PORT to find PIDs and kill -9 to force-kill
- The function silently handles cases where no process is found (empty catch block)
- Only targets processes in LISTENING state to avoid killing unrelated connections
- Force-kill (
/F on Windows, -9 on Unix) ensures stubborn processes are terminated
- Consider adding a small delay after killing if the port doesn't release immediately
Alternative: Manual Cleanup Commands
If you need to manually kill a process on a port:
Windows:
# Find the PID
netstat -ano | findstr :3847
# Kill it
taskkill /PID <pid> /F
# Or via PowerShell
Stop-Process -Id <pid> -Force
Unix/Mac:
lsof -ti:3847 | xargs kill -9
References
Converted and distributed by TomeVault — claim your Tome and manage your conversions.