glimfly
web updated

What is a port, and why won't my server start on 3000?

In one sentence:A port is a number from 0 to 65535 that tells a computer which program on it should receive a piece of network traffic, so only one program can listen on a given port at a time.

What it actually is

A port is a number, from 0 to 65535, that tells a computer which program should receive a piece of network traffic. Your machine has one address (on your own computer, that’s 127.0.0.1, also known as localhost), but dozens of programs can be running at once. The port number is how traffic gets routed to the right one.

Picture your machine as a single building with one street address. Each port is a numbered door in that building. Door 3000 might lead to your frontend dev server. Door 8000 might lead to your backend API. Same building, same address. Different doors, different occupants. Only one program can stand behind a given door at a time: when your app tries to listen on port 3000 and something else already claimed it, the operating system turns your app away instead of letting two programs answer the same door.

That’s why localhost:3000 and localhost:8000 behave like two completely separate apps even though they share the same address. The number after the colon decides which program actually answers.

Why your AI just did this

Your agent didn’t pick port 3000 out of nowhere. It’s a default baked into the tools it reaches for: Next.js and Nuxt start on 3000, Vite on 5173, Flask on 5000, Django on 8000. Ask Lovable, Cursor, or Claude Code to start your dev server, and it runs the framework’s default start command, binding to whatever port that framework ships with.

The collision happens when two things try to claim the same door. Maybe you ran npm run dev in one terminal tab, then asked your agent to start it again in another. Maybe a previous session crashed without shutting its server down cleanly, so an orphaned process still holds port 3000 in the background, invisible until the next start collides with it. Either way, the operating system enforces one listener per port. The second attempt loses.

When you’ll run into it

The error looks like this:

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

The triple colon isn’t a typo. By default, Node listens on the unspecified IPv6 address, written ::, when you don’t tell it which address to bind to. The port number gets appended right after it, so :: plus :3000 prints as :::3000. The part that actually matters is the ending: port 3000 is taken.

You’ll see this most often right after restarting a dev server that didn’t fully stop, running the same start command in two terminal tabs at once, or asking your agent to try again while an earlier attempt still runs in the background. It also shows up when two separate projects on your machine default to the same port and you try to run them side by side.

What to check

  • Read the exact port number the error names. That’s the door in question, and it isn’t always the one you expect.
  • On macOS or Linux, run lsof -i :3000 (swap in your port number) to see the process name and PID holding it.
  • On Windows, run netstat -ano | findstr :3000 to get the PID, then check Task Manager’s Details tab for that PID to see what it is.
  • Check what that command shows before touching anything. An old node process from a dev server you forgot to close is safe to end. A name you don’t recognize might belong to something unrelated, so leave it alone.
  • If it’s your own leftover server, stop it the clean way first: go to its terminal tab and press Ctrl+C, rather than reaching for kill -9 <PID> on macOS/Linux or taskkill /PID <PID> /F on Windows.
  • If you can’t tell what’s holding the port, skip the kill and run your app on a different one instead, with an environment variable your framework reads (PORT=3001) or a --port flag.

Ports 80 and 443 almost never show up in this error, because they’re the invisible defaults: your browser assumes 80 for http:// and 443 for https://, so it drops the number from the address bar entirely, and most operating systems block regular user accounts from binding under port 1024 without elevated permissions. You’ll meet 80 and 443 at deploy time on a real domain with HTTPS, not during local dev.

Say it like a dev

Instead of: “the server won’t start, it says something about address in use” Say: “I’m getting EADDRINUSE on port 3000. Can you check what’s using that port with lsof before killing anything, and tell me what it is?”

Instead of: “just kill whatever’s blocking it” Say: “Show me the process using port 3000 first. If it’s an old dev server of mine, stop it cleanly. Otherwise, run this app on a different port instead.”

Instead of: “why does my app have two different addresses” Say: “Confirm which port the frontend listens on and which port the backend API listens on, so I know which is which when I see an error.”

People actually ask

“Why do I get 'address already in use' when I try to start my server?”

Something is already listening on that exact port number, usually an old copy of your dev server that never shut down, or a second terminal tab running the same start command. Node reports this as EADDRINUSE because only one program can hold a given port on your machine at a time. Find out what's using the port before doing anything about it, since it might belong to something else entirely.

“What's the difference between localhost:3000 and localhost:8000?”

Both point at your own machine, but the number after the colon is a different port, so they're two separate programs even though the address is identical. Your frontend dev server might listen on 3000 while your backend API listens on 8000, and traffic sent to one never reaches the other.

“Is it safe to just kill whatever's using the port?”

Only once you've checked what it actually is. If it's an old process from your own dev server, closing it is fine; if you don't recognize the process name, killing it could shut down something unrelated to your project. Run lsof -i :PORT on macOS or Linux, or netstat -ano on Windows, to see the program and its PID before you touch anything.

Related terms

Go deeper

Checked against

free tool · no signup

Just met this in a real session? Glim reads it in plain words.

Paste what your agent just did, a git diff, your terminal, or its summary, and Glim tells you what changed and what to check before you ship. Nothing stored.

Explain my session →