You initialize your local development environment, execute your start command, and immediately hit a fatal error. Instead of a running application, the terminal outputs Error: listen EADDRINUSE: address already in use :::5000 or OSError: [Errno 48] Address already in use.
If you are encountering a "Port 5000 in use macOS" error without having any apparent backend servers running, the conflict is almost certainly originating from the operating system itself.
This guide details the exact root cause of this macOS localhost port conflict and provides concrete solutions to free up the port for your web development stack.
The Root Cause: macOS ControlCenter and AirPlay
Starting with macOS Monterey (macOS 12.0), Apple introduced the "AirPlay to Mac" feature. This allows users to cast video, audio, and screens from other Apple devices directly to their Mac screens.
To facilitate these incoming connections, macOS spins up a daemon tied to the ControlCenter process. This process aggressively binds to port 5000 on 0.0.0.0 (all IPv4 interfaces) and :: (all IPv6 interfaces).
Because the operating system reserves this port at boot, any web developer attempting to bind a local server—such as an Express.js API or a Flask port 5000 macOS server—will be blocked. The OS holds the priority, resulting in the EADDRINUSE exception.
You can verify this OS-level binding by executing the following diagnostic command in your terminal:
sudo lsof -i :5000
If the port is held by macOS natively, your output will look similar to this:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ControlCe 62190 macuser 32u IPv4 0x8a9b2c3d4e5f6g7h 0t0 TCP *:commplex-main (LISTEN)
ControlCe 62190 macuser 33u IPv6 0x1b2c3d4e5f6g7h8i 0t0 TCP *:commplex-main (LISTEN)
The Fix: How to Disable AirPlay Receiver Mac
If your application strictly requires port 5000 (often the case with legacy codebases, hardcoded CORS policies, or specific Docker Compose networks), you must disable the AirPlay Receiver Mac service.
The method depends on your specific macOS version.
For macOS Ventura, Sonoma, and Later:
- Open System Settings.
- Navigate to General in the left sidebar.
- Click on AirDrop & Handoff.
- Toggle off the switch next to AirPlay Receiver.
For macOS Monterey:
- Open System Preferences.
- Click on Sharing.
- Uncheck the box next to AirPlay Receiver in the left service list.
Once disabled, the ControlCenter process instantly releases port 5000. You do not need to restart your machine. You can immediately boot up your local development server.
Architectural Best Practice: Dynamic Port Allocation
Relying on hardcoded ports like 5000 is an anti-pattern in modern web development. System-level reservations, concurrent local microservices, and CI/CD pipeline tests frequently cause port collisions.
Instead of forcing developers to modify their OS settings, your codebase should read from environmental variables and fallback gracefully.
Modern Node.js / Express Implementation
Here is a production-ready approach for a Node.js server using ES Modules. It dynamically assigns the port and includes an error handler to catch EADDRINUSE events, attempting an alternative port automatically.
import express from 'express';
import http from 'http';
const app = express();
// Prefer the environment variable, fallback to 5000
const PORT = process.env.PORT || 5000;
app.get('/api/health', (req, res) => {
res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() });
});
const server = http.createServer(app);
server.on('error', (error) => {
if (error.syscall !== 'listen') throw error;
if (error.code === 'EADDRINUSE') {
console.error(`[Network Error] Port ${PORT} is already in use.`);
console.info('Attempting to bind to port 5001...');
// Fallback to a safe port if 5000 is blocked by macOS
server.listen(5001);
} else {
throw error;
}
});
server.listen(PORT, () => {
console.log(`Server successfully bound to http://localhost:${server.address().port}`);
});
Modern Python / Flask Implementation
By default, Flask binds to port 5000. If you do not want to disable AirPlay on your Mac, you should utilize a .flaskenv or .env file via python-dotenv to remap the port without altering the application code.
Create a .flaskenv file in your project root:
FLASK_APP=app.py
FLASK_ENV=development
# Shift the Flask port 5000 macOS default to 5001
FLASK_RUN_PORT=5001
If you are running the application programmatically via Python instead of the flask run CLI, inject the environment variable into the app.run method:
import os
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/health', methods=['GET'])
def health_check():
return jsonify({
"status": "healthy"
}), 200
if __name__ == '__main__':
# Retrieve port from environment, defaulting to 5001 to bypass AirPlay
port = int(os.environ.get('PORT', 5001))
# host='0.0.0.0' exposes the server to the local network
app.run(host='0.0.0.0', port=port, debug=True)
Common Pitfalls and Edge Cases
Zombie Node or Python Processes
If you disabled AirPlay Receiver and are still getting an EADDRINUSE error, a detached "zombie" process from a previous crashed debugging session is likely holding the port.
Find the Process ID (PID) holding the port:
lsof -ti :5000
Kill the specific PID returned by the previous command (replace 12345 with your actual PID):
kill -9 12345
You can combine these into a single command to instantly clear port 5000:
kill -9 $(lsof -ti :5000)
Docker Compose Port Mapping Conflicts
If you are running Docker Desktop on macOS, a container might be binding to port 5000. When configuring docker-compose.yml, it is standard practice to map the external port away from 5000 while keeping the internal container port as expected.
services:
api:
build: .
ports:
# Map host port 5001 to container port 5000
- "5001:5000"
environment:
- NODE_ENV=development
Summary
The macOS localhost port conflict on port 5000 is a direct result of Apple's AirPlay Receiver daemon (ControlCenter) claiming the port at the operating system level. While disabling the feature in macOS System Settings provides an immediate fix for local development, updating your application codebase to rely on environment variables (like process.env.PORT) ensures resilience against system-level port collisions across all developer machines and deployment environments.