Logo
Skip to main content
Development
5 min read

Expressjs Error: listen EADDRINUSE: address already in use :::port

D

Divya Mahi

February 28, 2024 · Updated February 28, 2024

Expressjs Error_ listen EADDRINUSE_ address already in use ___port

Express.js Error: listen EADDRINUSE: Address Already in Use :::Port

Introduction

Encountering the EADDRINUSE error in Express.js signals that the port your application intends to use is already occupied. This guide delves into the error, offering explanations, common scenarios accompanied by code snippets, and robust solutions to navigate and prevent such issues.

Understanding the Error

The EADDRINUSE error, short for "Address Already in Use," emerges when an Express.js server attempts to bind to a port already in use by another process. Each port can only be assigned to one process at a time, mirroring the exclusivity of physical addresses in the real world.

const express = require('express');
const app = express();

app.listen(3000, () => {
  console.log('Server on port 3000');
});

// Starting another instance on the same port
// Error: listen EADDRINUSE: address already in use :::3000

Diving Deeper

Initiating an Express.js server with app.listen(PORT) assigns the specified port to your app. If the port is preoccupied, the system fails to allocate it, causing an EADDRINUSE error.

Common Scenarios and Fixes with Example Code Snippets

Scenario 1: Duplicate Application Instances

Problematic Code: Launching multiple instances of the same application.

const express = require('express');
const app = express();

// Running the same script twice
app.listen(3000); // Second instance: EADDRINUSE

Solution: Identify and terminate duplicate processes.

const express = require('express');
const app = express();

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
}).on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${PORT} is already in use.`);
    console.error('Kill the other process or use a different port.');
    process.exit(1);
  }
});

Scenario 2: Development Tools like Nodemon

Problematic Code: Rapid restarts not releasing the port.

// Nodemon restarts — old process hasn't released the port yet
const app = express();
app.listen(3000);

Solution: Manually restart Nodemon or ensure it's configured to handle file changes gracefully.

const app = express();

// Handle graceful shutdown
const server = app.listen(3000);

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Server closed gracefully');
    process.exit(0);
  });
});

process.on('SIGINT', () => {
  server.close(() => process.exit(0));
});

Scenario 3: Port Conflicts with Other Services

Problematic Code: Another service is using your desired port.

// Port 3000 used by another service (e.g., React dev server)
app.listen(3000); // EADDRINUSE

Solution: Change your application's port or stop the other service.

// Use a configurable port
const PORT = process.env.PORT || 3001;

// Or find an available port
const net = require('net');
function getAvailablePort(start = 3000) {
  return new Promise((resolve, reject) => {
    const server = net.createServer();
    server.listen(start, () => {
      server.close(() => resolve(start));
    });
    server.on('error', () => resolve(getAvailablePort(start + 1)));
  });
}

getAvailablePort().then(port => {
  app.listen(port, () => console.log('Server on port', port));
});

Scenario 4: Unclean Server Shutdown

Problematic Code: The server didn't close properly, leaving the port in use.

// Process killed with SIGKILL — port not released
app.listen(3000);
// kill -9 <pid> leaves the port occupied temporarily

Solution: Implement graceful shutdown in your application.

const server = app.listen(3000);

// Enable port reuse
server.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.log('Port busy, retrying in 1 second...');
    setTimeout(() => {
      server.close();
      server.listen(3000);
    }, 1000);
  }
});

// Graceful shutdown
['SIGINT', 'SIGTERM'].forEach(signal => {
  process.on(signal, () => {
    server.close(() => process.exit(0));
  });
});

Scenario 5: System Reserved Ports

Problematic Code: Attempting to use a system-reserved port.

// Trying to use a privileged port without root
app.listen(80); // EACCES (not EADDRINUSE, but related)

Solution: Use a higher port number outside the reserved range.

// Use a non-privileged port and reverse proxy
const PORT = process.env.PORT || 3000;
app.listen(PORT);

// Use Nginx to proxy port 80 → 3000:
// server { listen 80; location / { proxy_pass http://localhost:3000; } }

Scenario 6: Microservices Port Collision

Problematic Code: Two microservices configured to use the same port.

// Multiple microservices all defaulting to port 3000
// service-a: app.listen(3000);
// service-b: app.listen(3000); // Collision!

Solution: Dynamically assign ports using environment variables.

// Assign unique ports per service
const SERVICE_PORTS = {
  'service-a': 3001,
  'service-b': 3002,
  'service-c': 3003,
};

const serviceName = process.env.SERVICE_NAME || 'service-a';
const PORT = process.env.PORT || SERVICE_PORTS[serviceName];

app.listen(PORT, () => {
  console.log(`${serviceName} running on port ${PORT}`);
});

Scenario 7: Docker Port Conflicts

Problematic Code: Docker container port conflicts with the host.

// Docker container maps port 3000 but host already uses it
// docker run -p 3000:3000 my-app → EADDRINUSE on host

Solution: Adjust Docker port mappings.

// Use different host port mapping
// docker run -p 3001:3000 my-app

// Or use docker-compose with random ports:
// ports:
//   - "3000"  # Random host port → container 3000

const PORT = process.env.PORT || 3000;
app.listen(PORT, '0.0.0.0', () => {
  console.log(`Container listening on port ${PORT}`);
});

Dockerfile:

Scenario 8: Zombie Processes

Problematic Code: Zombie processes occupying ports.

// Old process is a zombie occupying the port
// lsof -i :3000 shows a lingering process
app.listen(3000); // EADDRINUSE due to zombie

Solution: Reboot or manually clear zombie processes.

// Find and kill the process using the port
// Linux/Mac: lsof -i :3000 | grep LISTEN
// Then: kill -9 <PID>
// Windows: netstat -ano | findstr :3000
// Then: taskkill /PID <PID> /F

const PORT = process.env.PORT || 3000;
const server = app.listen(PORT);

server.on('error', (err) => {
  if (err.code === 'EADDRINUSE') {
    console.error(`Port ${PORT} in use. Run: lsof -i :${PORT}`);
    process.exit(1);
  }
});

Strategies to Prevent Errors

Dynamic Port Allocation: Let environments like cloud platforms assign ports.

Graceful Shutdown: Implement signal handling to ensure servers close properly.

Monitor Zombie Processes: Regularly check and clear zombie processes to free up ports.

Best Practices

Environment Variables for Ports: Facilitates easy port configuration adjustments.

Health Checks in Microservices: Helps identify and resolve port conflicts.

Comprehensive Logging: Essential for diagnosing EADDRINUSE errors promptly.

Conclusion

The EADDRINUSE error, while common, is manageable with a clear understanding and strategic approach. By recognizing potential scenarios, applying targeted solutions, and adhering to best practices, developers can mitigate port conflicts in Express.js applications, ensuring smooth operation and deployment.

Development
D

Written by

Divya Mahi

Building innovative digital solutions at Poulima InfoTech. We specialize in web & mobile app development using React, Next.js, Flutter, and AI technologies.

Ready to Build Your Next Project?

Transform your ideas into reality with our expert development team. Let's discuss your vision.

Continue Reading

Related Articles