Common causes
- A previous run of the dev server is still running in another terminal or in the background
- A crashed nodemon or ts-node-dev left an orphaned child process
- PM2 or systemd already runs the app, and you started it again by hand
- Your code calls app.listen() twice (for example in both app.js and server.js, or in tests)
- Another program uses the port (another framework's dev server, Docker container, Skype or AirPlay Receiver on 5000/7000 on macOS)
How to fix it
- Find the process using the port. On Linux/macOS run lsof -i :3000 (or sudo ss -ltnp | grep 3000). On Windows run netstat -ano | findstr :3000 and note the PID in the last column.
- Stop it cleanly. If it is your old dev server, stop it with Ctrl+C in its terminal, or kill <PID> on Linux/macOS, or taskkill /PID <PID> /F on Windows.
- Check process managers. Run pm2 list or systemctl status yourapp. If the app is already managed there, restart it with pm2 restart yourapp instead of launching another copy.
- Look for a double listen(). Search the code for .listen(. In tests, export the app without calling listen and let Supertest open its own port.
- Make the port configurable. Read the port from process.env.PORT with a fallback so you can run PORT=3001 npm start, and so hosts like cPanel/Passenger can assign one.
- Shut down gracefully. Handle SIGINT and SIGTERM and call server.close() so restarts by nodemon or PM2 release the port.
server.js
const port = Number(process.env.PORT) || 3000;
const server = app.listen(port, () => console.log(`Listening on ${port}`));
for (const sig of ['SIGINT', 'SIGTERM']) {
process.on(sig, () => server.close(() => process.exit(0)));
} How to stop it happening again
- Run one instance per port and manage production processes with PM2 or systemd only
- Always read the port from an environment variable
- Close the server on SIGINT/SIGTERM so watchers can restart cleanly