1. What Is It?
An IP address gets a packet to the right machine. A port gets it to the right process on that machine. Ports are 16-bit integers (0–65535) that the OS uses to multiplex network traffic across concurrent applications. Your web server on port 443 and your Postgres instance on port 5432 live on the same machine with the same IP — the port is what distinguishes them.
A socket is the OS abstraction for a network endpoint. It pairs an IP address with a port and an optional transport protocol ( or ). When your application calls connect("api.example.com", 443), the OS creates a socket, binds an ephemeral local port to it, and hands you a file descriptor. Everything you send or receive on that connection goes through that socket.
A backend server runs both an HTTPS service and a PostgreSQL database on the same machine with a single IP address. A client sends a TCP packet to that IP address destined for the database. What allows the operating system to route that packet to the PostgreSQL process rather than the HTTPS service?
2. How It Works
A connection is uniquely identified by four values: (source IP, source port, destination IP, destination port). This 4-tuple is called a socket pair, and it's how the OS demultiplexes incoming packets to the right process and connection.
Ephemeral ports: When your app opens an outbound connection, the OS assigns a random source port from the ephemeral range (typically 32768–60999 on Linux, 49152–65535 per IANA). This port exists only for the duration of the connection.
Listening socket vs. connected socket: A server calls bind(port) then listen() — this creates a single listening socket. For each incoming connection the OS creates a separate connected socket with a unique 4-tuple. That's why a server can handle thousands of concurrent connections all on port 443 simultaneously.
Well-known port assignments:
| Port | Protocol |
|---|---|
| 22 | SSH |
| 80 | HTTP |
| 443 | HTTPS |
| 3306 | MySQL |
| 5432 | PostgreSQL |
| 6379 | Redis |
| 27017 | MongoDB |
Concrete example — Docker port mapping:
When you run docker run -p 8080:80 nginx, Docker creates a rule that forwards traffic arriving at host:8080 to container:80. The container's nginx is listening on port 80 inside a private network; port 8080 is what's exposed on the host. Mismatching these is one of the most common Docker debugging situations.
Both endpoints identify a TCP connection
The client uses an ephemeral source port to reach nginx on port 443. A second client also uses port 443, and the four-tuple keeps the two connections apart.
The OS gives your app an ephemeral source port for this outbound TCP connection.
Text version
- The OS gives your app an ephemeral source port for this outbound TCP connection.
- The packet goes to nginx because it targets the server IP address and port four forty-three.
- A second client also connects to port four forty-three, but from a different IP address and port.
- Each connection has a unique four-tuple, so the OS keeps them apart even though both use port four forty-three.
A web server is handling 10,000 simultaneous HTTPS connections, all arriving on port 443. How does the operating system distinguish between these connections and deliver each incoming packet to the correct process/handler?
3. What SDEs Actually Need to Know
"Address already in use" errors:
EADDRINUSE means another process is already listening on that port, or a previous process's socket is in TIME_WAIT state. Use lsof -i :PORT or ss -tlnp | grep PORT to find what's holding it. For TIME_WAIT, either wait ~60s or set SO_REUSEADDR on the socket (most frameworks do this by default).
Port exhaustion:
Each outbound connection uses one ephemeral port. At ~30,000 available ephemeral ports, a service making many short-lived connections to the same destination IP:port can exhaust them. Symptoms: connection errors despite the target being healthy. Fix: (reuse sockets) or increase the ephemeral range (net.ipv4.ip_local_port_range).
0.0.0.0 vs 127.0.0.1 bind address:
127.0.0.1— loopback only, unreachable from outside the host. Use for services that should not be externally accessible.0.0.0.0— all interfaces, including the network-facing NIC. Use it (or a specific non-loopback interface IP) if you want the service reachable from other machines or containers.- In Kubernetes, this is why your service must bind
0.0.0.0(or the pod IP). The pod IP is different from127.0.0.1, and kube-proxy routes traffic to the pod IP.
Privileged ports (< 1024):
On Linux, binding a port below 1024 requires root or the CAP_NET_BIND_SERVICE capability. The exception is when the network namespace lowers net.ipv4.ip_unprivileged_port_start (Docker 20.10+ sets it to 0 inside containers). Without that, production containers have three options. They run as root (bad practice), use a (nginx binds 443 as root, then drops privileges), or are granted the specific capability.
The bind address determines pod IP reachability
Kube-proxy routes traffic to the pod IP. A loopback-only listener cannot receive that traffic, but a service bound to all interfaces listens on the pod IP too.
Traffic to the pod IP cannot reach this service because it listens only on loopback.
Text version
- Traffic to the pod IP cannot reach this service because it listens only on loopback.
- Binding to all interfaces lets the service receive that traffic, because it also listens on the pod IP.
- Kube-proxy sends traffic to the pod IP, not to loopback, so a Kubernetes service must bind to all interfaces or the pod IP.
A backend service running inside a Kubernetes pod binds to 127.0.0.1:8080 during startup. After deployment, kube-proxy is configured to route external traffic to the pod's IP address, but requests consistently fail to reach the service. What is the most likely cause?
4. Tradeoffs & Decisions
Assigning custom ports:
When you choose a port for your service, avoid the IANA well-known range (0–1023) and registered services your stack might use. Using a port already in use by Redis or a monitoring agent causes EADDRINUSE and confusing failures. Tools like ss -tlnp show all listening ports (ss -ulnp for ).
vs. per-request connections: Opening a new connection for every request costs: lookup + TCP 3-way handshake + handshake ≈ 1–3 RTTs of overhead. holds open connections and reuses them. The tradeoff is resource consumption (idle connections hold OS memory and file descriptors) and complexity (pool sizing, health checks). For most database clients and clients to shared services, pooling is the right default.
Firewall rules at the port level:
Security groups and iptables rules operate on port ranges. Opening port 0-65535 from all sources (0.0.0.0/0) defeats network-layer security. Principle of least privilege: only open the specific ports needed, and only to the blocks that need access.
Reuse connections and budget for idle sockets
Connection pooling avoids repeated connection setup. The tradeoff is idle socket resources and pool management.
A connection pool reuses open sockets instead of opening a new TCP connection for each request.
Text version
- A connection pool reuses open sockets instead of opening a new TCP connection for each request.
- Per-request setup costs one to three round trips, while idle pooled sockets hold memory and file descriptors.
- For database and HTTP clients to shared services, pooling is the right default, but the pool needs sizing and health checks.
Your backend service makes hundreds of short-lived requests per second to a shared PostgreSQL database. A colleague suggests opening a fresh TCP connection for each request to keep things simple. What is the primary performance cost of this approach compared to using a connection pool?
5. Interview Cheat Sheet
Key sentences:
- "A port multiplexes traffic to the right process; a socket is the OS abstraction combining IP + port + protocol into a file descriptor your app reads and writes."
- "A connection is uniquely identified by a 4-tuple: source IP, source port, destination IP, destination port — this is how the OS handles thousands of concurrent connections all arriving on port 443."
- "Ephemeral ports are temporary source ports the OS assigns to outbound connections; exhausting them (~30k by default) causes connection failures even when the destination is healthy."
- "Binding to
127.0.0.1makes a service reachable only from the same host;0.0.0.0opens it on all interfaces."
Common follow-ups:
Q: Why can a server accept many connections on the same port? A: The listening socket accepts new connections; each accepted connection becomes a separate connected socket distinguished by the unique 4-tuple (client IP, client port, server IP, server port). The OS routes incoming packets to the right socket based on this 4-tuple.
Q: What is TIME_WAIT and why does it matter?
A: After a connection closes, the OS keeps the socket in TIME_WAIT for 2× the maximum segment lifetime (typically 60s on Linux) to ensure delayed packets from the old connection don't corrupt a new one on the same 4-tuple. When restarting a server rapidly, the old port may still be in TIME_WAIT — SO_REUSEADDR lets the new process bind it immediately.
Q: How does affect port usage? A: A gateway rewrites the source IP and port of outbound packets from private hosts so they appear to come from the NAT's public IP. It tracks the mapping in a connection table to route responses back. NAT gateways also have port limits — many cloud NAT implementations cap concurrent connections per source IP.