# Port 5000 Already in Use on macOS? It's AirPlay Receiver

Port 5000 already in use on macOS? AirPlay Receiver (ControlCenter) binds 5000 and 7000. Confirm with lsof, disable AirPlay Receiver, or change your Flask, Docker, or Node port.

- Published: 2026-07-25
- Author: Vikas Kapadiya
- Category: Web Development
- Reading time: 7 min
- Canonical URL: https://kapadiya.net/blog/port-5000-already-in-use-macos/
- Tags: macOS, Port 5000, AirPlay Receiver, ControlCenter, EADDRINUSE, address already in use, Monterey, Flask, Docker, Node, Local Development

You start a local server and macOS answers with something like this:

```text
Error: listen EADDRINUSE: address already in use :::5000
```

Or, with Docker:

```text
Error starting userland proxy: listen tcp4 0.0.0.0:5000: bind: address already in use
```

You did not launch another app. `kill` does nothing useful. Restarting the terminal does nothing. Port 5000 is simply taken — and “what is using port 5000 on my Mac?” is the right next question.

On macOS Monterey (12) and later (Ventura, Sonoma, Sequoia…), the usual culprit is not a leftover Node process. It is **AirPlay Receiver**, running under **Control Center** (`ControlCenter` / `ControlCe` in `lsof`), which binds **TCP 5000** (and often **7000**) by default. Apple documents both ports as used by AirPlay.

This post covers how to confirm it, disable AirPlay Receiver to free the port, or keep AirPlay and change your app port instead.

## Why port 5000 is always in use on macOS

Port 5000 is a popular default:

- Flask’s development server
- Many Docker Compose samples and demo APIs
- Node, Express, and assorted static servers and internal dashboards

AirPlay Receiver, introduced as a first-class Mac feature in Monterey, turns your Mac into an AirPlay target (phone → Mac screen/speakers). That service needs network listeners. Apple’s port list maps **TCP 5000** and **TCP 7000** to AirPlay.

So two ecosystems quietly chose the same number:

| Side | Uses port 5000 for |
| --- | --- |
| macOS | AirPlay Receiver (`ControlCenter`) |
| Dev tools | Local HTTP / API servers |

If you upgraded past Big Sur, AirPlay Receiver was often already on. You only notice when a framework tries to bind the same port and throws `EADDRINUSE` / “address already in use.”

## What is using port 5000? Confirm with lsof

Before changing system settings, verify who owns the port:

```bash
lsof -nP -iTCP:5000 -sTCP:LISTEN
```

If AirPlay is involved, you will see something like:

```text
COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ControlCe   423 you    33u  IPv4 0x…               0t0  TCP *:5000 (LISTEN)
ControlCe   423 you    34u  IPv6 0x…               0t0  TCP *:5000 (LISTEN)
```

`ControlCe` is Terminal truncating **ControlCenter**. That process is part of macOS; it is not your Flask app and not a rogue Docker container.

Also check 7000 if that port shows up in compose files or other tools:

```bash
lsof -nP -iTCP:7000 -sTCP:LISTEN
```

Same `ControlCenter` process usually means the same root cause.

> **Do not fight ControlCenter with kill**
> Killing `ControlCenter` is a dead end. macOS restarts it, and AirPlay Receiver comes back with it. Free the port by turning the feature off, or stop competing for 5000 by changing your app’s port.

## Fix 1: Disable AirPlay Receiver (free ports 5000 and 7000)

If you do not use your Mac as an AirPlay target, turn the feature off. Ports release immediately; no reboot required.

### macOS Ventura, Sonoma, Sequoia, and later

1. Open **System Settings**
2. Go to **General → AirDrop & Handoff** (on some builds this is labeled **AirDrop & Continuity**)
3. Turn **AirPlay Receiver** off

Faster path: open System Settings and search for `AirPlay`. Older blog posts still say **System Preferences → Sharing** — that path was Monterey-era; on current macOS the toggle lives under AirDrop & Handoff.

### macOS Monterey

1. Open **System Preferences**
2. Open **Sharing**
3. Uncheck **AirPlay Receiver**

Then re-check the port:

```bash
lsof -nP -iTCP:5000 -sTCP:LISTEN
```

No output means nothing is listening. Start your server again.

Trade-off: other Apple devices can no longer AirPlay **to this Mac**. Outbound AirPlay **from** the Mac (to a TV or speaker) is a different feature and is not what this toggle is for.

## Fix 2: Keep AirPlay Receiver, change your Flask / Docker / Node port

If you actually use AirPlay Receiver, do not disable it for a local server. Point the app at another port. Anything free above 1024 works; **5001**, **5050**, **8000**, and **8080** are common choices. Avoid **7000** while AirPlay Receiver is on.

### Flask

```bash
flask run --port 5001
```

Or in code:

```python
if __name__ == "__main__":
    app.run(port=5001)
```

Persist it in the environment:

```bash
export FLASK_RUN_PORT=5001
```

### Node / Express

```bash
PORT=5001 node server.js
# or
npx serve -l 5001
```

### Next.js

```bash
PORT=5001 next dev
# or
next dev -p 5001
```

### Docker Compose

```yaml
services:
  api:
    ports:
      - "5001:5000" # host:container
```

That maps host **5001** to container **5000**, so the image can keep its internal default without fighting ControlCenter on the Mac.

### Rails / other Ruby apps

```bash
rails server -p 5001
# or
bundle exec puma -p 5001
```

### Generic Python HTTP server

```bash
python3 -m http.server 5001
```

## The quieter failure mode: blank pages and odd HTTP responses

Sometimes the bind does not fail. You already have something on 5000, open `http://localhost:5000`, and get a blank page, a refusal, or a response that is clearly not your app.

That can happen when the browser hits **AirPlay’s listener** instead of your process — or when two things fight for the same port over time. If `lsof` shows `ControlCenter` on 5000, stop debugging your framework first. Fix the port ownership, then retest.

## What about “just use another default forever”?

For personal machines, either fix is fine. For shared repos and onboarding docs, **changing the default port** is usually kinder:

1. New teammates on macOS hit the conflict less often.
2. Nobody has to disable a system feature they might want.
3. Docker port maps stay explicit and boring.

A one-line note in the README helps:

```markdown
## Local dev

Default API port is **5001** (not 5000). macOS AirPlay Receiver
often occupies 5000/7000 via Control Center.
```

If your team standardizes on 5000 for historical reasons, document the AirPlay toggle next to the setup steps so the next person does not spend an hour on `EADDRINUSE`.

## Quick checklist: port 5000 already in use on macOS

1. See `EADDRINUSE` / “address already in use” on **5000** (or **7000**).
2. Run `lsof -nP -iTCP:5000 -sTCP:LISTEN` to see what is using port 5000.
3. If the command is `ControlCe` / ControlCenter → AirPlay Receiver.
4. Either:
   - **System Settings → General → AirDrop & Handoff → AirPlay Receiver** off (Monterey: **Sharing**), or
   - Run Flask, Docker, Node, etc. on **5001** (or another free port) and update Compose/README.
5. Confirm with `lsof` again, then start the server.

## Takeaway

Port 5000 is not “broken” on modern macOS. Apple claimed it for AirPlay Receiver through Control Center, and a large slice of the local-dev world (Flask defaults, Docker samples, Node apps) still assumes 5000 is free. Once you know that, the mystery is gone: diagnose with `lsof`, free the port by disabling AirPlay Receiver, or stop competing and bind somewhere else.

If you only remember one command from this post, make it this:

```bash
lsof -nP -iTCP:5000 -sTCP:LISTEN
```

When `ControlCenter` shows up, you are not debugging your stack — you are negotiating with the OS.
