Short answer: lsof lists open files and the processes that hold them, and on Linux sockets count as files. So sudo lsof -i :8080 shows what is using a port, sudo lsof -p PID everything one process has open, sudo lsof +D /path who is using a directory, and sudo lsof +L1 deleted files that still take up disk space. Run it with sudo: without root, lsof cannot read the open files of other users’ processes, so their sockets are simply missing from lsof -i.
sudo lsof -i :8080 # what uses port 8080
sudo lsof -p 1234 # every file PID 1234 has open
sudo lsof +L1 # deleted files that are still openMinimal Debian and Ubuntu installs do not include it: sudo apt install lsof, or sudo dnf install lsof on Rocky and AlmaLinux. The examples marked as real output were run on Debian 12 with lsof 4.95.0, against a test process that held a 10 MB file open; the rest follows the lsof man page.
lsof cheat sheet
| Task | Command |
|---|---|
| What uses a port, in any state | sudo lsof -i :8080 |
| Only the listening TCP socket | sudo lsof -nP -iTCP:8080 -sTCP:LISTEN |
| Every listening TCP port | sudo lsof -nP -iTCP -sTCP:LISTEN |
| All network connections | sudo lsof -nP -i |
| Connections to or from one host | sudo lsof -nP -i @203.0.113.10 |
| Files of one process | sudo lsof -p 1234 |
| Files of every process with a name | sudo lsof -c nginx |
| Files of one user | sudo lsof -u www-data |
| Everyone except root | sudo lsof -u ^root |
| Who has this file open | sudo lsof /var/log/nginx/access.log |
| Who uses anything under a directory | sudo lsof +D /var/www |
| Deleted files still holding space | sudo lsof +L1 |
| PIDs only, for scripts | sudo lsof -t -i :8080 |
| Combine conditions with AND | sudo lsof -a -u www-data -i |
| Repeat every 2 seconds | sudo lsof -r 2 -i :443 |
Add -n and -P to almost everything that touches the network: -n stops lsof looking up host names and -P keeps port numbers as numbers (443 instead of https). Both make it faster and the output easier to grep.
Reading lsof output
This is real output for the test process, a sleep started with a log file open on descriptor 3. The test directory is shortened to /tmp/lsof-test, and the locale files mapped into memory are left out:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sleep 3897237 root cwd DIR 8,1 4096 3673507 /tmp/lsof-test
sleep 3897237 root rtd DIR 8,1 4096 2 /
sleep 3897237 root txt REG 8,1 43888 672 /usr/bin/sleep
sleep 3897237 root mem REG 8,1 1926232 9755 /usr/lib/x86_64-linux-gnu/libc.so.6
sleep 3897237 root mem REG 8,1 215000 9751 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
sleep 3897237 root 0r CHR 1,3 0t0 4 /dev/null
sleep 3897237 root 1w CHR 1,3 0t0 4 /dev/null
sleep 3897237 root 2w CHR 1,3 0t0 4 /dev/null
sleep 3897237 root 3w REG 8,1 10485760 3673780 /tmp/lsof-test/app.log- COMMAND, PID, USER: the process. COMMAND is cut to nine characters unless you widen it with
+c 15. - FD: a descriptor number with its mode (
rread,wwrite,uboth), or a word for things that are not descriptors:cwdthe working directory,rtdthe root directory,txtthe program itself,mema file mapped into memory, such as a shared library. - TYPE:
REGa regular file,DIRa directory,CHRa character device,IPv4andIPv6network sockets,unixa local socket,FIFOa pipe. - SIZE/OFF: the file size in bytes, or an offset where size means nothing;
0t0is offset 0, written in decimal. - NODE and NAME: the inode and the path, or for a socket the protocol and the addresses.
Only the numbered lines are file descriptors, which is why lsof -p PID | wc -l overcounts. To count descriptors against a limit, use ls /proc/PID/fd | wc -l, as shown in too many open files. Plain lsof with no options is worse still: on Debian 12 it adds TID and TASKCMD columns and repeats the files of every thread, so its line count says little about anything.
lsof -i: which process uses a port
sudo lsof -i :443 # every socket with port 443 on either end
sudo lsof -nP -iTCP:443 -sTCP:LISTEN # just the service listening on 443
sudo lsof -nP -iUDP:53 # UDP sockets on port 53
sudo lsof -nP -i4TCP:1-1024 # IPv4 TCP sockets on ports 1 to 1024COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1340 root 6u IPv4 24118 0t0 TCP *:443 (LISTEN)
nginx 1341 www-data 6u IPv4 24118 0t0 TCP *:443 (LISTEN)
nginx 1341 www-data 12u IPv4 91822 0t0 TCP 203.0.113.10:443->198.51.100.7:52114 (ESTABLISHED)
https 2210 _apt 5u IPv4 93310 0t0 TCP 203.0.113.10:41870->192.0.2.80:443 (ESTABLISHED)-i :443 matches the port on either end of a socket, so it also lists your server’s own outgoing connections to port 443 somewhere else, like the apt download (its https helper, running as _apt) in the last line. When you are looking for the program that holds a port, add the state filter, written as the man page does with the protocol in -i too: -iTCP:443 -sTCP:LISTEN. The general form of the address is [4|6][protocol][@host][:port], every part optional, so -i4TCP:1-1024 is one address and not two options. ss answers the same question faster and is preinstalled on current distributions; both are covered in checking open ports on Linux.
Files of a process, a command or a user
sudo lsof -p 1340 # one PID (several: -p 1340,1341)
sudo lsof -c php-fpm # every process whose name starts with php-fpm
sudo lsof -u www-data # everything a user has open
sudo lsof -u ^root -i # network files of everyone except rootThe -a trap: lsof joins options with OR
By default lsof lists a file if it matches any of your options, not all of them. sudo lsof -u www-data -i therefore prints every file of www-data plus every network socket of every user, which is rarely what anyone means. -a switches to AND, and it applies to all options at once, wherever you put it:
sudo lsof -a -u www-data -i # network sockets of www-data only
sudo lsof -a -p 1341 -d 0-20 # descriptors 0 to 20 of one processOn the test system, lsof -a -p PID -c cron for the sleep process returned nothing, as it should: no process is both that PID and named cron. Exclusions such as -u ^root are the exception; they always apply first.
Who is using a file or a directory
sudo lsof /var/lib/dpkg/lock-frontend # who holds the apt lock
sudo lsof +D /srv/data # anything open below /srv/data, recursively
sudo lsof +d /srv/data # only files directly in /srv/data
sudo lsof /mnt/backup # a mount point: every open file on that filesystem+D walks the whole tree and checks every file in it, which takes a while on a directory with millions of files; +d stays at one level. Naming a mount point instead of a directory makes lsof list every open file on that filesystem, which is the quickest answer to umount: target is busy. The usual culprits are a shell whose working directory is inside the mount (FD cwd) and a service writing a log there. fuser -vm /mnt/backup, from psmisc, gives the same answer in a shorter form. The apt lock line is the first step in Could not get lock.
Deleted files that still use disk space: lsof +L1
Deleting a file only removes its name. As long as a process keeps the file open, the kernel keeps the data, and the space stays used: df reports the disk full while du cannot find the files. +L1 lists open files with a link count below one, which means files that no longer have a name. This is the test process again, after rm app.log:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
sleep 3897237 root 3w REG 8,1 10485760 0 3673780 /tmp/lsof-test/app.log (deleted)All 10 MB still sit on the disk, owned by PID 3897237 through descriptor 3. On a real server the list is sorted by size to find the big ones; with +L1 the size is the seventh column, because lsof adds NLINK after it:
sudo lsof -nP +L1 | sort -k7 -rn | head
sudo lsof -nP +aL1 / # only deleted files on the root filesystemTwo ways to get the space back:
- Restart or reload the process that holds the file. Once it closes the descriptor, the space is free. For a log, a reload usually makes the program reopen its log files, which is what logrotate’s postrotate scripts trigger.
- Truncate it through /proc when a restart is not possible:
sudo truncate -s 0 /proc/PID/fd/FD. On the test process,: > /proc/3897237/fd/3took SIZE/OFF from 10485760 to 0 without stopping it.
DEL in the output is a related case: a library or program file that was replaced by an update while a running process still has the old copy mapped into memory. sudo lsof -n | grep -w DEL after an upgrade shows which services still run old code and need a restart. The full disk-full checklist, with this step in it, is in No space left on device, and why df and du disagree explains the numbers.
lsof and kill
-t prints only PIDs, one per line and without a header, which is made for passing on to kill:
sudo lsof -nP -iTCP:8080 -sTCP:LISTEN # look first
sudo kill $(sudo lsof -t -iTCP:8080 -sTCP:LISTEN)
sudo lsof -t +D /mnt/backup | xargs -r sudo kill # everything still using a mountLook before you kill. lsof -t -i :8080 without the state filter also returns any client that happens to be connected to port 8080 somewhere else, and a kill with no PIDs at all fails with an error; xargs -r skips the call when lsof finds nothing. On the test system, kill $(lsof -t -a -p PID -d 3) ended the sleep process as expected. Which signal to send, and what to do when a process ignores it, is in how to kill a process in Linux.
When lsof is slow or noisy
- It reads every process. Narrow the search with
-p,-ior a path, and add-n -P. A fulllsofon a busy server takes a while and prints far more than you can read. - It hangs on a dead network mount. A stuck NFS or SSHFS mount can block lsof;
-bavoids the kernel calls that block, at the price of less detail. - Warnings about other filesystems. Lines starting with
lsof: WARNING: can’t stat()are mounts lsof is not allowed to look into;-whides them.-talready implies-w.
lsof on macOS
macOS ships lsof, and the -i, -p, -u, -t and +L1 commands above work the same (there is no /proc to truncate through, though). It is the standard way to find the program behind a port on a Mac, because the macOS netstat has no process column by default: sudo lsof -nP -iTCP -sTCP:LISTEN lists every listening port with its program.
Frequently asked
How do I find which process is using a port with lsof?
sudo lsof -i :8080 lists every socket with port 8080 on either end, including outgoing connections. For only the program listening on it, use sudo lsof -nP -iTCP:8080 -sTCP:LISTEN; -n and -P keep addresses and ports numeric, which is also faster.
How do I find deleted files that still use disk space?
sudo lsof +L1 lists open files whose link count is below one, meaning they were deleted while a process still holds them open. Restart or reload that process to free the space, or truncate a log through /proc/PID/fd/FD when a restart is not possible.
Why does lsof show more lines than a process has open files?
lsof also lists the working directory, the root directory, the program binary and memory-mapped libraries, none of which uses a file descriptor, and plain lsof with no options repeats the files for every thread. Count real descriptors with ls /proc/PID/fd | wc -l instead.
Why does lsof -u www-data -i show other users' sockets?
lsof joins selection options with OR by default, so that command lists every file of www-data plus every network socket on the system. Add -a to require all conditions at once: sudo lsof -a -u www-data -i.