skip to content

On a Linux host you need to confirm whether anything is listening on TCP port 8080 and, if so, which process owns it. How do you do that with `ss`, what does each letter in `ss -tulpn` select, and why does the process column sometimes come back empty?

level: juniorimportance: must knowfreq 82%

answer

  1. socket statistics, not netstat
  2. five independent switches
  3. the p column needs privilege
  4. UDP never says LISTEN

basics

~20 s

Run ss -tulpn as root: t selects TCP, u UDP, l listening sockets only, p the owning process, n numeric ports. The process column is empty for sockets owned by other users unless you are root.

solid answer

~40 s

`ss` is the iproute2 socket inspector that replaced `netstat`; it reads socket state from the kernel over netlink rather than parsing `/proc`. `ss -tulpn` reads as five independent switches: `-t` TCP, `-u` UDP, `-l` only listening sockets, `-p` show the owning process, `-n` print numeric ports and addresses instead of resolving them. The Process column prints something like `users:(("nginx",pid=1234,fd=6))`, which is the answer to "who has my port". To narrow it I'd use `ss -tlnp 'sport = :8080'`. If I run it unprivileged the sockets still appear, but the process column is blank for anything I don't own — mapping a socket to a PID means reading other processes' file descriptors, so that needs root. `-n` also matters operationally: without it a broken resolver makes the command crawl.

go deeper

for a junior

Be able to type ss -tulpn from memory and say what each letter does, then point at the Process column and name the owning process. Knowing ss replaced netstat is expected.

for a middle

Explain that ss reads socket state over netlink rather than parsing /proc, why -n avoids resolver stalls, and why the process column needs root. Show the filter form ss -tlnp 'sport = :8080'.

for a senior

Demonstrate that this is step one of a triage, not the answer: state clearly what a bound socket does and does not prove, and account for network namespaces so you do not declare a container's listener missing.

for a principal

Frame it as tooling standardisation: which socket-inspection commands belong in your runbooks and base images, whether debug tooling ships in production images at all, and how operators inspect namespaced listeners without a shell in every container.

## What `ss` is `ss` — "socket statistics" — ships in the **iproute2** package and is the default socket inspector on every current Linux distribution. It asks the kernel for socket state through the netlink `sock_diag` interface instead of parsing `/proc/net/tcp` line by line the way `netstat` did, which is why it stays fast on a host holding tens of thousands of sockets. `netstat` belongs to the older **net-tools** package, which is deprecated and frequently not installed at all on minimal images and containers, so `ss` is the command to reach for by default. ## Decoding `-tulpn` The flags are independent and can be combined in any order: - `-t` — TCP sockets. - `-u` — UDP sockets. - `-l` — listening sockets only (without it you also get established connections). - `-p` — show the process holding each socket. - `-n` — numeric: do not translate port numbers to service names or addresses to hostnames. Other switches worth knowing: `-a` (all sockets, listening and not), `-x` (Unix domain sockets), `-4` / `-6` to restrict the address family, `-s` for a one-screen summary of socket counts by protocol and state. ## Reading the output ```bash sudo ss -tulpn # Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process # tcp LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=1234,fd=6)) # udp UNCONN 0 0 127.0.0.53:53 0.0.0.0:* ``` The columns are the socket family (`Netid`), the state, two queue counters, the local and peer address:port pairs, and the process. Two details trip people up here: - **UDP sockets never show `LISTEN`.** UDP is connectionless, so a bound UDP socket shows as `UNCONN`. `-l` still selects it, because from `ss`'s point of view it is a socket waiting for traffic. - **`Peer Address:Port` of `0.0.0.0:*`** on a listening row simply means "no specific peer" — it is not a wildcard peer the socket is talking to. ## Why the process column can be empty Enumerating sockets is unprivileged, but mapping a socket to the process that owns it is not: it means walking `/proc/<pid>/fd` for every process and matching socket inodes. An ordinary user may only do that for their own processes, so `ss -tulpn` run without `sudo` prints the sockets with a blank Process column for anything owned by root or another user. If you see listeners but no owners, add `sudo` before concluding anything. The same question has a container twist. Inside a container `ss -p` only sees processes in that container's PID namespace, and the PID it prints is the namespaced one — the same process has a different PID on the host. And if the container was started without the capability to inspect other processes, the column stays blank there too. ## Narrowing the search `ss` has a small filter language, which beats piping through `grep` because it filters in the right place and cannot match the wrong column: ```bash ss -tlnp 'sport = :8080' # who is listening on 8080 ss -tnp 'dst 10.0.0.5' # my connections to that host ss -tn state established # only established TCP ``` Note that `sport` is the right key for a *listening* socket. Filtering listeners on `dport` returns nothing, because a listening socket has no destination port — that is a common self-inflicted "nothing is listening" conclusion. ## Traps worth naming in an interview - **Forgetting `-l`.** `ss -tunp` on a busy host prints thousands of established connections and the listener you wanted scrolls past. - **Forgetting `-n`.** Name resolution turns `:22` into `:ssh` and, more importantly, makes the command hang for seconds per lookup when DNS is the thing that is broken. - **Treating "the port is listening" as "the service is healthy".** A socket can be bound by a process that is deadlocked, out of worker threads, or refusing every request. `ss` proves the socket exists, nothing more. - **Assuming `ss` sees inside containers.** A listener inside a network namespace does not appear in the host's `ss` output; you need `nsenter` into that namespace, or run `ss` in the container. The honest summary of what this command buys you: it answers "is the port bound, and by whom" definitively and in one second, which is why it is the first command in almost every Linux network triage.

  • You run `ss -tulpn` and see nothing on port 8080, but the application logs say it started successfully. What would you check next?
    First confirm you looked in the right network namespace — a process in a container or netns has listeners invisible to the host's `ss`. Then check whether the app bound a different port than configured, or bound and then crashed (`systemctl status`, the unit's logs). Finally re-run with `sudo` and without `-l`, in case it bound but is in a non-listening state.
  • Why is `ss` faster than `netstat` on a host with a very large number of sockets?
    `netstat` reads and parses text out of `/proc/net/*`, which the kernel regenerates as it is read, so cost grows badly with socket count. `ss` uses the netlink `sock_diag` interface, a binary protocol that lets it request exactly the families and states it wants and stream them, so it stays usable where `netstat` takes minutes.
  • How would you get a one-line picture of how many sockets exist and in what states, without listing them all?
    `ss -s` prints a summary: total sockets, and per-protocol counts broken down by state. It is the fast way to spot, for example, an enormous number of sockets in a single state on a box where you suspect connection churn, before you decide which detailed query to run.

saying these in an interview costs you the question

  • Reaching for netstat and assuming it is installed
  • Claiming ss needs no privileges to show PIDs
  • Saying a listening UDP socket shows state LISTEN
  • Treating a bound port as proof the service is healthy
  • Filtering listeners with dport instead of sport

context