VK
Web DevelopmentFeatured

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.

On this page
  1. Why port 5000 is always in use on macOS
  2. What is using port 5000? Confirm with lsof
  3. Fix 1: Disable AirPlay Receiver (free ports 5000 and 7000)
  4. macOS Ventura, Sonoma, Sequoia, and later
  5. macOS Monterey
  6. Fix 2: Keep AirPlay Receiver, change your Flask / Docker / Node port
  7. Flask
  8. Node / Express
  9. Next.js
  10. Docker Compose
  11. Rails / other Ruby apps
  12. Generic Python HTTP server
  13. The quieter failure mode: blank pages and odd HTTP responses
  14. What about “just use another default forever”?
  15. Quick checklist: port 5000 already in use on macOS
  16. Takeaway

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

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

Or, with Docker:

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:

lsof -nP -iTCP:5000 -sTCP:LISTEN

If AirPlay is involved, you will see something like:

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:

lsof -nP -iTCP:7000 -sTCP:LISTEN

Same ControlCenter process usually means the same root cause.

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:

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

flask run --port 5001

Or in code:

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

Persist it in the environment:

export FLASK_RUN_PORT=5001

Node / Express

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

Next.js

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

Docker Compose

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

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

Generic Python HTTP server

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:

## 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:

lsof -nP -iTCP:5000 -sTCP:LISTEN

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

End of note

More field notes when the next problem is specific enough to be useful.