Ports
The question is almost always "what is holding port 8080". takes a port on its own or with a protocol in front of it.
| Command | What it does |
|---|---|
| Every process using port 8080, listening or connected | |
| TCP only ( for UDP) | |
| The server on that port, not the clients talking to it | |
| Just the PID, ready to pipe into another command | |
| A range of ports | |
| Every listening TCP port on the machine | |
| Port 8080, but only what user deploy owns |
Free the port, then confirm it is free:
sudo lsof -i :8080 # who has it
kill $(sudo lsof -ti :8080) # ask them to stop
kill -9 $(sudo lsof -ti :8080) # make them stopWithout you see only your own processes, so a port held by root reads as free. For a plain list of listeners, is faster; see the netstat and ss sheet.
Files and Mounts
The other everyday job: something will not unmount, or a file will not delete.
| Command | What it does |
|---|---|
| Which processes have this exact file open | |
| Every open file on that filesystem, when the path is a mount point | |
| Everything open under a directory, walking the whole tree | |
| Same, but one level down only and much faster | |
| Treat the path as a plain file, not as a filesystem | |
| Who is using a block device | |
| Files open on NFS mounts | |
| The same answer from fuser, which can also kill with |
lsof reads a path as a filesystem when it matches a mount point from , and as a single file otherwise. That is why lists hundreds of rows and lists one.
Processes and Users
| Command | What it does |
|---|---|
| Files open by any command starting with nginx | |
| Exact command name, written as a regex | |
| Everything except sshd (a leading negates) | |
| Everything PID 1234 has open | |
| Several PIDs at once | |
| Every PID of a process by name | |
| Files open by one user | |
| Everything except root's files | |
| How many files a process has open, to compare with | |
| The working directory of every process | |
| Only stdin, stdout and stderr |
Deleted Files
The classic case: says the disk is full, says it is not. A process is holding a file that no longer has a name, and the space comes back only when the descriptor closes.
| Command | What it does |
|---|---|
| Open files with no directory entry left, the ones eating the space | |
| Same, limited to one filesystem | |
| The grep version, works on any lsof build | |
| What one process is still holding |
sudo lsof +L1 # find the holder: note its PID and FD
sudo systemctl restart rsyslog # cleanest fix, restart the owner
: > /proc/1234/fd/7 # or empty the file in place, no restartDeleting a log file that a service is writing to is what causes this. Truncate with or hand it to logrotate instead.
Network Connections
| Command | What it does |
|---|---|
| Every open network file on the machine | |
| The same, without DNS and service name lookups | |
| IPv4 only ( for IPv6) | |
| Live TCP connections, nothing that is merely listening | |
| Connections to or from one host | |
| One host and port together | |
| Only what node has on the network | |
| UNIX domain sockets, the socket files under | |
| Repeat the listing every 5 seconds |
lsof names the process behind a connection; tcpdump shows what that connection is sending.
Output Columns
Half of using lsof is reading what it printed.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 root 6u IPv4 28134 0t0 TCP *:80 (LISTEN)
nginx 1234 root 11w REG 253,0 184320 264781 /var/log/nginx/access.log
sshd 902 root 3u IPv6 21883 0t0 TCP 10.0.0.5:22->10.0.0.9:51544 (ESTABLISHED)| Column | What it holds |
|---|---|
| Program name, cut to nine characters unless you pass | |
| Process ID, the column prints on its own | |
| The user the process runs as | |
| Descriptor number and mode, or a keyword (see below) | |
| file, directory, device or terminal, pipe, / socket, socket file | |
| Device major and minor numbers, or the kernel address of a socket | |
| File size in bytes, or the read/write offset written as and a number | |
| Inode number for a file; for a socket it is the protocol, or | |
| The path, or for a connection |
FD is the column people misread. It is either a number with a mode letter or one of these keywords:
| FD value | Meaning |
|---|---|
| , , | Descriptor number plus mode: read, write, read and write |
| A trailing or is a lock held on the file | |
| The process's current working directory | |
| Its root directory, which differs after a chroot | |
| Program text: the executable being run | |
| A memory-mapped file, nearly always a shared library | |
| A mapped file that has since been deleted | |
| lsof could not read , usually a permission problem |
Output Flags
| Flag | What it does |
|---|---|
| PIDs only, no header and no columns, made for | |
| Skip DNS lookups, show numeric addresses | |
| Skip service name lookups, show port numbers | |
| Print full command names instead of nine characters | |
| Machine-readable output, one field per line with a leading letter | |
| Only the PID, command and name fields, for scripts | |
| Suppress the noise from fuse and gvfs mounts | |
| Avoid kernel calls that can block, for when a network mount is hung | |
| Show numeric UIDs instead of usernames (fixes ) | |
| Repeat every 5 seconds until Ctrl+C | |
| , | Usage summary; version and build details |
together is worth making a habit. It removes both lookups and turns a command that takes seconds into one that returns straight away.
Combining Filters
Filters of different kinds OR together by default, which catches almost everyone out. turns the whole line into AND.
| Command | What you get |
|---|---|
| Everything deploy has open, plus everyone's network files | |
| Only deploy's network connections | |
| Only the network files of PID 1234 | |
| nginx processes running as www-data, nothing else | |
| Listening TCP sockets that root does not own | |
| What deploy has open under | |
| nginx or php-fpm: filters of the same kind always OR, even with |
applies to the entire command line. There is no way to AND one pair of options while leaving another pair as OR; run two commands instead.
Gotchas
- Without , lsof quietly shows only your own processes. An empty result is not proof that a port or file is free.
- lsof is not installed on minimal Debian, Ubuntu and RHEL images. or .
- matches listeners and connections to that port. Add when you want the server alone.
- There is no JSON output. is the parseable format: one field per line, each prefixed by a letter, and keeps it to PID, command and name.
- lines are normal on machines with fuse or gvfs mounts. hides them and the results are unaffected.
- Inside a container, lsof sees that container's namespace only. Run it on the host to find who really owns a port.
- On macOS, is the way to map a port to a process, because netstat there cannot show process names at all.