Ports and Services
A port is how one IP address hosts many conversations. It is also the first thing to check when something "cannot connect", and the thing people most often misread.
The mechanics
A TCP or UDP header carries a 16-bit source port and a 16-bit destination port: 0–65535.
| Range | Name | Use |
|---|---|---|
| 0–1023 | Well-known | Assigned by IANA to core services; binding needs privilege (or CAP_NET_BIND_SERVICE) |
| 1024–49151 | Registered | Applications and vendor products |
| 49152–65535 | Dynamic / ephemeral | Client side of outbound connections |
The destination port is chosen by the client and names the service. The source port is chosen by the kernel's ephemeral allocator and identifies this conversation, so 500 browsers can talk to one server on 443 at once.
ss -lntp # what is listening, on which address
ss -tn state established # conversations, with queue depths
cat /proc/sys/net/ipv4/ip_local_port_range # Linux ephemeral range (32768 60999)
sysctl net.ipv4.ip_local_port_range # same, as a setting
Ports you will actually meet
| Port | Protocol | Service | Common mistake |
|---|---|---|---|
| 22 | TCP | SSH / SFTP | Exposed to the internet with passwords enabled |
| 25 | TCP | SMTP relay | Blocked by most providers; use 587 to submit mail |
| 53 | TCP+UDP | DNS | Firewalled "because UDP is enough" — large answers and zone transfers use TCP |
| 67/68 | UDP | DHCP server / client | DHCP relay missing on a routed VLAN |
| 80 | TCP | HTTP | Left open "for redirects" with no redirect configured |
| 123 | UDP | NTP | Blocked, so certificates and logs quietly drift |
| 161/162 | UDP | SNMP / traps | Community string left at public |
| 179 | TCP | BGP | Firewalled between peers; session never establishes |
| 389/636 | TCP | LDAP / LDAPS | Cleartext bind allowed |
| 443 | TCP+UDP | HTTPS / HTTP/3 | UDP 443 blocked, so QUIC silently falls back to TCP |
| 445 | TCP | SMB | Reachable from a user VLAN, which is how ransomware spreads |
| 514 | UDP | Syslog | Unencrypted, unauthenticated, usually unread |
| 3306/5432/1433 | TCP | MySQL / Postgres / SQL Server | Bound to 0.0.0.0 and reachable from the internet |
| 3389 | TCP | RDP | Internet-exposed RDP is a standing invitation |
| 6379/27017/9200 | TCP | Redis / MongoDB / Elasticsearch | Default "no auth, no TLS" deployments found by scanners within hours |
Full lists exist, but these are the ones that cause incidents.
Listening address matters as much as port
127.0.0.1:8080 only this host can connect
0.0.0.0:8080 every interface, including the public one
[::]:8080 every IPv6 interface (often dual-stack with 0.0.0.0)
192.168.1.5:8080 only that interface
A database "not exposed to the internet" that binds 0.0.0.0 is exposed to the internet the moment a firewall rule or a security group is wrong. Bind to loopback or the private interface and the mistake becomes impossible.
Reading a connection refusal correctly
| Observation | Meaning |
|---|---|
Connection refused (RST) |
Something answered: no listener on that port, or a firewall rejected with RST |
| Timeout, no reply | Packet dropped: firewall DROP rule, route missing, host down, or asymmetric path |
Connection reset by peer mid-session |
Listener died, or a middlebox decided to kill the flow |
| Works from one host, not another | Source-based firewall rule, NAT, or routing difference — not the service |
address already in use |
Another process holds the port; ss -lntp names it |
Refused vs timeout is the single most useful distinction in troubleshooting, because it tells you whether your packets are being answered at all.
Ephemeral port exhaustion
Every outbound connection consumes a source port for the duration, plus TIME_WAIT afterwards. With ~28,000 ports per destination IP and a workload that opens and abandons connections rapidly, you can run out:
ss -s | head -3 # total sockets, TIME_WAIT count
netstat -an | grep -c TIME_WAIT
Symptoms: intermittent cannot assign requested address under load, rising latency, retries. Fixes are connection pooling, HTTP keep-alive, more destination IPs, or widening the port range — in that order, because pooling is the real fix.
Where it goes wrong
- Assuming one port per service. HTTP/3 is UDP 443, DNS is both UDP and TCP 53, and many products listen on dozens of ports plus a dynamic range.
- Fixing the application firewall and forgetting the network one. Two layers, two sets of rules; the one that blocks is often the one nobody owns.
- Opening a port "to test" and never closing it. The test that took thirty seconds becomes a permanent ingress rule.
- Confusing "listening" with "reachable".
ssproves the process; it says nothing about routing or policy. - Copying a docker-compose from the internet with
- "5432:5432". That publishes the database on every interface by default.
Related Notes
- IP TCP UDP — the headers that carry ports
- TLS And HTTPS — what happens after the port opens
- Firewalls And ACLs — which layer drops the packet
- Packet Analysis Wireshark — seeing RST vs silence