Guide
Port number ranges explained: which ports you can use and which you should not
September 14, 20263 min read
There are 65,536 port numbers, and they are not all equal. IANA splits them into three ranges, and the operating system treats one of them differently. Knowing which is which explains a couple of errors that otherwise look random.
The three ranges
0 to 1023, the well known ports. Reserved for standard services. 22 is SSH, 80 is HTTP, 443 is HTTPS, 53 is DNS. These need root to bind on macOS and every other Unix.
1024 to 49151, the registered ports. Organisations can register a port for a specific service, which is how 3306 became MySQL, 5432 became PostgreSQL and 6379 became Redis. Nothing enforces this. It is a convention that stops everyone colliding, not a rule the kernel applies.
49152 to 65535, the dynamic or ephemeral ports. Not assigned to anything. This is the pool the system draws from when a program needs a temporary port, which mostly means outbound connections.
On macOS you can see the ephemeral range the kernel actually uses:
sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
# net.inet.ip.portrange.first: 49152
# net.inet.ip.portrange.last: 65535
Why binding port 80 fails
Try it as a normal user and you get:
PermissionError: [Errno 13] Permission denied
Nothing is misconfigured. Ports below 1024 are privileged, and only root may bind them. The original reasoning was trust: if anyone could listen on 80, any user on a shared machine could impersonate the web server everyone connects to.
Most people meet this when they try to run a dev server on 80 to skip typing a port. The options are to use sudo, which you should not do casually for a dev server, to put something in front that already has permission, or to accept a high port. The third one is why every framework defaults to a high port.
Where the dev server defaults come from
You have seen these enough to recognise them on sight:
| Port | Usual occupant |
|---|---|
| 3000 | Node, Rails, Next.js, Create React App |
| 4200 | Angular |
| 5000 | Flask, and AirPlay Receiver on macOS |
| 5173 | Vite |
| 5432 | PostgreSQL |
| 6379 | Redis |
| 8000 | Django, Python’s http.server |
| 8069 | Odoo |
| 8080 | Tomcat, and the traditional alternative HTTP port |
All comfortably above 1024, all just convention. Nothing stops you running Postgres on 7777.
The macOS specific trap: port 5000
This one catches people regularly. On modern macOS, AirPlay Receiver listens on port 5000 by default. If you start a Flask app on 5000 and it fails, or behaves strangely, the operating system got there first.
You can turn AirPlay Receiver off in System Settings under General, or simply use another port. Port 7000 has had similar conflicts, which is why Bosun excludes 5000 and 7000 from its monitored range by default.
Choosing a port for your own service
A few practical rules.
Stay above 1024 unless you have a real reason, so you never need sudo.
Avoid the ephemeral range, 49152 and up. If you bind a fixed service there, the system might already have handed that port to an outbound connection, and you get an intermittent conflict that is miserable to reproduce.
That leaves roughly 1024 to 49151, which is plenty. Pick something memorable and not already famous.
Seeing which ranges are in use right now
lsof -i -P -n -sTCP:LISTEN
You will usually find a handful of things below 1024 owned by system processes, your own servers in the low thousands, and possibly some high numbered entries from Docker or other tools that allocate dynamically.
If a port you expected to be free is not, the conflict is nearly always another instance of your own service. We wrote about fixing that on port 3000 specifically.
Bosun monitors a configurable range, 3000 to 9999 by default, and shows what owns each port without the command. macOS 14 or later, 14 day trial.
See this instead of typing it
Bosun lives in your menu bar and shows every open port on your Mac, live, mapped to the process behind it. One-click kill, SIGTERM first. Useful the first time this happens. Genuinely useful the fifth time it happens in one afternoon.