TL;DR: Ports are numbered doors on your computer (0–65535). When AI runs your app on localhost:3000, the :3000 IS the port — it's the specific door your app is listening behind. Different apps use different ports so they don't collide: your frontend on 3000, your API on 4000, your database on 5432. If you get an EADDRINUSE error, it means something else already has that door open.

Why AI Coders Need to Understand Ports

You ask Claude to build you a Next.js app. It generates the code, tells you to run npm run dev, and says: "Your app is running on http://localhost:3000." You open that URL, your app appears, and everything feels like magic.

Then you ask it to add an Express API backend. It generates that too, runs it, and says: "API server running on port 4000." Now you've got two things running on two different numbers, and you're starting to wonder — why 3000? Why 4000? What are these numbers? Can I change them? What happens if they're the same?

This isn't abstract networking theory. This is something that breaks your workflow at least once a week if you don't understand it. You'll close your laptop, come back, try to start your app, and get a cryptic error about the address being "in use." You'll try to run two projects at once and one of them won't start. You'll deploy to a server and nothing works because the port isn't exposed.

Every AI coding tool — Claude, Cursor, Copilot, Windsurf — assumes you know what ports are. They assign them, reference them, and configure them without explaining the basics. This article fills that gap in about 13 minutes.

What Ports Actually Are (The Apartment Building Analogy)

Your computer has an IP address — think of it as a street address for a building. But a building has many apartments, and mail needs to reach the right one. Ports are the apartment numbers.

Your computer can run dozens of network services simultaneously: a web server, a database, an email client, Spotify streaming music, Slack receiving messages. They all share the same IP address but need a way to keep their traffic separate. That's what port numbers do — they route incoming data to the correct application.

Port numbers range from 0 to 65,535. That's not arbitrary — it's the largest number that fits in 16 bits of data (the size of the port field in a TCP/UDP packet header). In practice, they break down into three ranges:

Range Name What It Means
0–1023 Well-Known Ports Reserved for standard services (HTTP on 80, HTTPS on 443, SSH on 22). On Mac/Linux, you need admin privileges to use these.
1024–49151 Registered Ports Used by specific applications by convention (PostgreSQL on 5432, MongoDB on 27017). Your dev servers live here too — 3000, 4000, 5173, 8080.
49152–65535 Dynamic/Ephemeral Ports Temporarily assigned by your OS for outgoing connections. When your browser connects to a website, it uses a random port from this range on your end.

When you see localhost:3000, here's what that URL actually means: localhost = your own computer (IP address 127.0.0.1), and :3000 = port 3000 on that computer. Your browser is connecting to your own machine, apartment 3000, where your Next.js app happens to be listening.

Key Insight

Port 3000 isn't special. Next.js just picks it as a default. You could run the exact same app on port 9999 or port 42069 — it would work identically. The port number is just a label that lets your browser find the right application on your machine.

Real Scenario: AI Builds a Full-Stack App with Three Services

Here's something that happens every day: you ask Claude to build a full-stack project. It generates a Next.js frontend, an Express API, and connects to a PostgreSQL database. When you start everything up, you see three different port numbers in your terminal:

✓ Next.js app running on http://localhost:3000
✓ Express API running on http://localhost:4000
✓ PostgreSQL listening on port 5432

Why three different ports? Because each service is a separate program that needs its own door. If they all tried to use port 3000, only the first one would succeed — the other two would crash with an EADDRINUSE error.

Here's how they talk to each other:

  1. Your browser connects to localhost:3000 — that's the Next.js frontend showing you the UI
  2. The Next.js app makes API calls to localhost:4000 — that's the Express backend handling business logic
  3. The Express API connects to localhost:5432 — that's PostgreSQL storing and retrieving data

Three programs, three ports, one machine. Each port is a separate communication channel. The data flows: browser → 3000 (frontend) → 4000 (API) → 5432 (database) and back again.

This is why AI assigns different ports — it's not arbitrary, it's required. And once you see it this way, the architecture of every full-stack project makes immediate sense. When you look at a Docker compose file and see port mappings like 3000:3000 and 5432:5432, you'll know exactly what those numbers mean.

Common Ports Every AI Coder Should Recognize

You don't need to memorize all 65,535 ports. You need to recognize about a dozen. Here are the ones you'll see constantly when building with AI tools:

Port Service When You'll See It
80 HTTP Standard web traffic. When you visit http://example.com without specifying a port, your browser uses 80 automatically. In production, nginx listens here.
443 HTTPS Encrypted web traffic. https://example.com uses port 443 by default. SSL/TLS certificates live here. This is what your users actually connect to.
22 SSH Secure shell access to remote servers. When you SSH into a VPS or deploy via SFTP, you're connecting on port 22.
3000 React / Next.js / Node Default dev server port for Next.js, Create React App, and many Express templates. The most common port you'll see in AI-generated code.
5173 Vite Default dev server for Vite (used by SvelteKit, modern Vue, and some React setups). If AI uses Vite as the bundler, you'll see this instead of 3000.
8080 Alternative HTTP Common alternative when port 80 requires admin access. Many Java apps, Spring Boot, and development servers use 8080.
5432 PostgreSQL Default port for PostgreSQL databases. When AI sets up a database connection string, this port is usually in there.
27017 MongoDB Default port for MongoDB. You'll see this in connection strings like mongodb://localhost:27017/mydb.
6379 Redis Default port for Redis, an in-memory data store used for caching and session management.
3306 MySQL Default port for MySQL/MariaDB databases.
Pattern to Notice

Web-facing services use low numbers (80, 443). Developer tools use mid-range numbers (3000, 5173, 8080). Databases use their own conventions (5432, 27017, 6379). Once you recognize the pattern, you can glance at a port number and immediately know what kind of service is behind it.

Port Conflicts: The EADDRINUSE Error Explained

This is the #1 port-related error that trips up AI coders. You try to start your app and get:

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

In plain English: something else is already using port 3000. Only one program can listen on a given port at a time. If port 3000's door is already open, your app can't open it again.

This happens constantly because of a few common scenarios:

  • You didn't properly stop your last dev session. You closed the terminal tab but the Node.js process kept running in the background. It's still holding port 3000 hostage.
  • You're running two projects that both default to port 3000. Next.js project A is running, and you try to start Next.js project B — same default port, instant conflict.
  • A crashed process left a zombie. Your app crashed but the process didn't fully exit. The OS still thinks the port is in use.
  • Another tool grabbed the port. Some desktop apps (like certain VPNs or antivirus software) occasionally grab common development ports.

The fix is straightforward — either kill whatever is using the port, or run your app on a different port. We'll cover exactly how to do both in the debugging section below.

⚠️ Don't Panic

EADDRINUSE is annoying but harmless. It's not a bug in your code. It's not a security issue. It just means: "That door is taken, pick another one." Most frameworks let you change the port with an environment variable like PORT=3001 npm run dev.

What AI Gets Wrong About Ports (3 Common Mistakes)

1. Hardcoding Port Numbers

AI loves to generate code like this:

// ❌ What AI often generates
const app = express();
app.listen(4000, () => {
  console.log('Server running on port 4000');
});

The port is hardcoded as 4000. This works fine in development, but it's a problem for two reasons: you can't change it without editing source code, and in production you often need the port to be set by the hosting platform (Railway, Render, Heroku all assign dynamic ports).

// ✅ What it should generate
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

This reads the port from an environment variable first, and only falls back to 4000 if none is set. Simple change, but it makes your app deployable anywhere.

2. Forgetting to Expose Ports in Docker

When AI generates a Dockerfile, it sometimes forgets the EXPOSE instruction or misses the port mapping in docker-compose.yml. Your app runs inside the container on port 3000, but from the outside — your browser, your other services — that port is invisible.

# ❌ Missing port exposure
FROM node:22
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "start"]

# ✅ With proper port exposure
FROM node:22
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]

And in docker-compose.yml, you need the port mapping:

services:
  web:
    build: .
    ports:
      - "3000:3000"  # host port : container port

The 3000:3000 syntax means: "Connect port 3000 on my machine to port 3000 inside the container." Without this mapping, the container's port is completely isolated.

3. Not Explaining the Relationship Between Ports and URLs

AI rarely explains that when you visit https://myapp.com — with no port number visible — you're actually connecting to port 443. The port is always there; the browser just hides it when it's the default (80 for HTTP, 443 for HTTPS). In production, nginx receives traffic on port 443 and forwards it internally to your app on port 3000. The user never sees the 3000 — but it's still there, behind the scenes.

The Full Picture

https://myapp.com = https://myapp.com:443. They're identical. The browser omits :443 because it's the default for HTTPS. In development, you see localhost:3000 because 3000 is not a default — so the browser has to show it.

How to Debug Port Problems

When you get EADDRINUSE or need to figure out what's running on which port, here are the exact commands you need. Copy-paste these directly into your terminal.

Find What's Using a Port (macOS / Linux)

# Find what's using port 3000
lsof -i :3000

# Output looks like:
# COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
# node    12345  you    22u  IPv6  0x1234   0t0  TCP *:hbci (LISTEN)
#
# The PID (12345) is the process ID you need

Find What's Using a Port (Windows)

# Find what's using port 3000
netstat -ano | findstr :3000

# Output shows the PID at the end of the line
# Then kill it:
taskkill /PID 12345 /F

Kill a Process by PID

# Mac/Linux — graceful stop
kill 12345

# Mac/Linux — force kill (if graceful doesn't work)
kill -9 12345

# Nuclear option: kill ALL Node.js processes
killall node

Run Your App on a Different Port

# Next.js
npx next dev -p 3001

# Vite
npx vite --port 3001

# Express / Node.js (if using env var)
PORT=3001 node server.js

# Create React App
PORT=3001 npm start

See All Listening Ports at Once

# Mac — see everything that's listening
lsof -iTCP -sTCP:LISTEN -n -P

# Linux
ss -tlnp

# Windows
netstat -an | findstr LISTENING
💡 Pro Tip

When you get EADDRINUSE, paste the entire error message into Claude Code or Cursor and say: "This port is in use. Give me the command to find and kill whatever is using it on [Mac/Windows/Linux]." AI is excellent at generating the right diagnostic commands for your specific OS.

How Ports Work in Production

Development and production handle ports differently, and this catches a lot of AI coders off guard.

In development, you directly access your app's port: localhost:3000. Simple. One app, one port, one browser tab.

In production, a reverse proxy like nginx sits in front of your app. Here's the flow:

User's browser
  → https://myapp.com (port 443 — the default HTTPS port)
    → nginx (listens on 443, has SSL certificate)
      → forwards to localhost:3000 (your Node.js app, hidden from the public)
        → responds back through the same chain

Your app still runs on port 3000 internally. But users never connect to port 3000 directly — nginx handles the public-facing connection on port 443 and proxies the request behind the scenes. This is why you don't see :3000 in production URLs.

This also means: never expose your app's port directly to the internet. If port 3000 is open on your firewall, anyone can bypass nginx (and its security features) and hit your app directly. In production, only ports 80 and 443 should be publicly accessible. Everything else stays internal.

What to Learn Next

Ports are one piece of the networking puzzle. Now that you understand them, here's where to go next:

FAQ

A port is a numbered endpoint (0–65535) on your computer that identifies a specific application or service. Think of your computer's IP address as a street address and ports as individual apartment numbers. When your browser visits a website, it connects to port 443 (HTTPS). When your AI-built app runs on localhost:3000, the 3000 is the port number — the specific door your app is listening behind.

Port 3000 is a convention, not a rule. React (Create React App), Next.js, and many Node.js frameworks default to port 3000 because it's in the unprivileged range (above 1024), easy to remember, and unlikely to conflict with system services. You can change it to any available port — your AI tool just picks 3000 because that's the framework's default. Use PORT=3001 npm run dev or the framework's port flag to change it.

EADDRINUSE means "Error: Address Already In Use." Another process is already listening on the port your app wants to use. To fix it: find the process with lsof -i :3000 (Mac/Linux) or netstat -ano | findstr :3000 (Windows), note the PID, and kill it with kill -9 <PID> (Mac/Linux) or taskkill /PID <PID> /F (Windows). Or simply change your app to use a different port like 3001.

No. Only one application can listen on a specific port at a time on the same network interface. If you try to start a second app on the same port, you'll get an EADDRINUSE error. This is why AI tools assign different ports to different services — your frontend on 3000, your API on 4000, your database on 5432. Each needs its own exclusive door.

Yes, but differently than in development. In production, a reverse proxy like nginx listens on ports 80 (HTTP) and 443 (HTTPS) — the standard web ports — and forwards traffic to your app running on an internal port like 3000. Users never see the port number because 80 and 443 are the defaults browsers use automatically. You also need to expose the right ports in Docker containers and configure firewall rules to only allow traffic on ports 80, 443, and 22 (SSH).