lsof Command: Tutorial & Examples
List open files — and on Unix, everything is a file.
What It Is
lsof stands for list open files, and the trick to loving it is taking that name absolutely literally. On Unix, everything is a file — a process talking to disk has a file open, a process listening on port 8080 has a file open, a process reading from your keyboard has a file open, a process talking to a database over the network has a file open. So lsof doesn't just list files in the desktop sense — it lists every open network connection, every listening port, every pipe between two programs, every memory-mapped library, every device node, every deleted-but-still-held log file on the entire system. One uniform table.
If you've never touched a server before, this is the command you'll fall in love with about a month in — the first time something says "Address already in use" and you can't figure out what's holding port 8080, or the first time df says the disk is 100% full but du only finds half that much. Both of those are lsof questions with one-line answers, and we'll get to them. Along the way we'll explain every column, learn the small handful of flags that do 90% of real work, and pick up the "everything is a file" idea that runs through all of Unix.
Your First Look
The killer one-liner, the one most admins discover six months too late:
lsof -i :22
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 892 root 3u IPv4 18402 0t0 TCP *:ssh (LISTEN)
sshd 14021 root 4u IPv4 48522 0t0 TCP server:ssh->client:52758 (ESTABLISHED)
ssh 2397752 arndt 3u IPv4 48522 0t0 TCP xps:52758->server-01:ssh (ESTABLISHED)
Three lines, and they answer the question every newcomer asks: what's on port 22, and who's connected to it? Top row: the sshd daemon is listening (*:ssh (LISTEN)). Middle row: someone is currently connected to it. Bottom row: my outgoing ssh session that initiated the connection. One command, full picture — process name, PID, user, protocol, endpoints, state.
The bare command — lsof with no arguments — dumps every open file on the whole system, which on a busy box is tens of thousands of rows and not what you want. You almost always run lsof with one filter: a port, a PID, a path, or a user. That's the shape of every real session.
How I Use It
There are about four lsof questions I find myself asking on real servers, and they cover almost everything.
"What's listening on this port?" This is the most common, by a wide margin. lsof -i :8080 and you instantly know which process is holding the port — usually the answer to "I can't restart my server, address already in use." The -i flag selects internet sockets (TCP and UDP), and the :PORT syntax narrows to one port; lsof -i tcp:8080 adds the protocol; lsof -i @1.2.3.4 filters by remote host. This one flag is why lsof ended up installed on every server on earth.
"What is this process actually touching?" lsof -p PID lists every file (sockets, pipes, libraries, regular files, devices — all of it) that one process has open. Read top-to-bottom and you see the whole shape of a program: which config files it loaded at startup (mem rows), which log files it's writing now (REG with a path under /var/log), which databases it's talking to (TCP rows), which directory it's running in (cwd). Pair with strace when you need to see what it's doing right now: lsof shows state, strace shows behavior. They're complements.
"Who's locking this file/directory and why can't I unmount it?" lsof /path/to/file lists every process with that file open. lsof +D /mnt/data does the same for everything under a directory (it's slow — it walks the whole tree — hence the *SLOW?* warning in lsof -h). The classic case is umount refusing with "target is busy" — lsof +D /mnt names the culprit in two seconds. (fuser is the older, terser cousin that does the same job in fewer characters; both work.)
"Where did my disk space go?" The one that feels like magic the first time: lsof | grep deleted. See the Gotcha — it's the trick that recovers your full disk.
Pro Tip
lsof -i :PORTis the answer to about 80% of "what's holding this port?" questions you'll ever ask. Memorize it before any otherlsofinvocation. Add-P(don't translate port numbers to service names — show:8080, not:http-alt) and-n(don't reverse-DNS the IPs — much faster, no hangs on stale DNS) and you get a snappy, unambiguous answer every time:lsof -i :8080 -Pn.
The Columns Explained
Every column in default output, in order. Once you know these, every row reads like a sentence.
COMMAND— the program holding the file (first 9 chars by default; widen with+c 0for the full name). Same name you'd see inps'sCOMMANDcolumn.PID— the process ID, the number you feed tokillonce you've found the culprit.USER— who owns the process. A process running as root holding files it shouldn't is a security smell.FD— the file descriptor: usually an integer (0,1,2,3, …), sometimes a name. The integer is the slot number the kernel handed the process when it opened the file —0/1/2are always stdin/stdout/stderr, the rest are everything else the program opened in order. A trailing letter is the access mode:rread,wwrite,uread-write. Named slots are special:cwdcurrent working directory,rtdroot directory,txtthe program binary itself,mema memory-mapped file (usually a shared library — you'll see a wall of these per process, it's normal),DELa file that's been deleted but still mapped.TYPE— what kind of file. The vocabulary here is the whole "everything is a file" lesson made visible:REGa regular file on disk,DIRa directory,CHRa character device (like/dev/tty),BLKa block device (a disk),FIFOa pipe,unixa Unix-domain socket,IPv4/IPv6a network socket,a_inodean anonymous inode (eventfd, signalfd, the things epoll is built on). Whatever the kernel hands out a file descriptor for has a type here.DEVICE— the device numbers (major,minor) of the underlying filesystem, or the kernel's address for sockets. Mostly noise; useful for distinguishing two filesystems with the same path.SIZE/OFF— file size in bytes, or the current read/write offset for things being actively streamed (e.g.0t52428800= byte offset 52428800 — that's what0tmeans, decimal offset).NODE— the inode number for regular files; the protocol (TCP,UDP) for sockets;PIPEfor pipes. Surprisingly diagnostic — two paths sharing one inode are hard-linked.NAME— what it actually is. For regular files: the full path. For sockets:local-addr:port->remote-addr:port (STATE), e.g.*:ssh (LISTEN),xps:52758->server-01:ssh (ESTABLISHED). For pipes:pipeplus an ID. For deleted files: the original path with(deleted)appended — the most useful four characters in this whole command.
Reading It by Example
The handful of lsof recipes that turn into muscle memory.
Who's holding port 8080?
lsof -i :8080 -Pn
The single most common reason to run lsof. -Pn keeps it fast and shows numbers (no DNS lookups, no /etc/services translation). ss -ltnp is the modern alternative — same answer, different tool family.
What's every listening port on this box?
lsof -i -P -n | grep LISTEN
The fast audit before you trust a fresh server. Every row is something the world can reach. Anything unexpected (a MySQL bound to *:3306 instead of 127.0.0.1:3306?) is a finding.
Every file one program has open:
lsof -p 2323
Or by name, no PID lookup needed:
lsof -c nginx
-c matches the process command (substring), so this shows files held by every nginx worker at once.
Which process is locking this file/mount?
lsof /var/log/app.log
lsof +D /mnt/data
The first is instant; the second walks the whole tree (slow but thorough) and is what unblocks a stuck umount.
The deleted-files trick — the one that recovers a full disk:
lsof | grep deleted
If df says the disk is full but du can't find the data, this is almost always why. See the Gotcha — it deserves its own callout.
Everything owned by one user:
lsof -u www-data
Pair with -i to narrow to that user's sockets: lsof -u www-data -i. Great for "what is this service actually doing?"
Cheat Sheet
The flags worth memorizing — in roughly the order you'll need them:
lsof -i :PORT— what's on this port? (Add-Pnfor speed and clarity.)lsof -i— every network connection on the box.lsof -i tcp/-i udp/-i @host— narrow by protocol or remote host.lsof -p PID— every file one process has open.lsof -c name— every file every process with that command name has open.lsof /path/to/file— who's got this file open.lsof +D /path— who's got anything under this directory open (slow — walks the tree).lsof -u user— files held by one user (or-u ^rootto exclude).lsof | grep deleted— the disk-full rescue trick.lsof -P— show port numbers, not service names.lsof -n— no DNS reverse lookups (much faster, no hangs).lsof -t— terse: PIDs only, perfect forkill $(lsof -t /var/log/app.log).lsof -a— combine filters with AND instead of the default OR (e.g.lsof -a -u alice -i= alice's network connections, not "alice's files OR all network connections").lsof -r 5— repeat every 5 seconds; turnslsofinto a live monitor.
How You'll Actually Use It
In real life lsof lives in three moments. The port detective: something says "address in use," you type lsof -i :PORT, you find the culprit, you kill it (or, better, restart its systemd unit). The unmount unblocker: umount /mnt refuses with device busy, you run lsof +D /mnt, you find the shell someone left cd'd into the mount point, you wave at them in Slack. The disk-space rescue: df shows 100% full, du can't account for it, lsof | grep deleted reveals the log file someone rm'd while it was still being written.
Outside those, it's a reference tool. When strace shows you a process hammering file descriptor 17 and you need to know what file is FD 17, lsof -p PID is the answer. When htop shows you a process and you press l, htop is just launching lsof -p for you — same data, prettier launcher.
Gotchas
- The "deleted but still held open" trick — and why it matters. When a program has a file open and someone
rms the file, the directory entry vanishes but the file stays on disk — the kernel only frees the blocks when the last open file descriptor closes. So a service writing a 50 GB log, someonerms the log to "free space,"dfstill shows the disk full,ducan't find a thing.lsof | grep deletednames the culprit. The fix isn't anotherrm— there's nothing left to remove. You either restart the process (closes the FD, kernel reclaims the blocks) or, the elegant trick, truncate the file via its/procentry:: > /proc/PID/fd/17(where 17 is the FD column). This is thelsofskill that pays for the whole command. - Without root, you mostly see your own files.
lsofreads through/proc, and/proc/<PID>/fdis only readable by the process's owner (or root). Run it withoutsudoand you see every process's name but only the open-file details for processes you own. For real diagnosis:sudo lsof. (On hardened boxes withhidepidmounted on/proc, even less.) lsofis slow on big boxes, and warning-noisy. Each row is a/procread; tens of thousands of files means seconds of wall time. The+Drecursive scan is much slower (itstats every file). Expect harmlessWARNING: can't stat()lines for sysfs and per-servicesystemd-private-*tmpfs mounts you don't have permission to enter — they're noise from the tool being thorough, not real errors.- The default is OR, not AND.
lsof -u alice -ilists "alice's files OR all network sockets" (i.e. everything with a network socket plus alice's local files). To narrow with AND you must pass-a:lsof -a -u alice -i. Counterintuitive and bites everyone once. -c NAMEmatches the truncated command name (first 15 characters, same kernel limit that bitespgrep). Long binary names need partial matches orlsof -p $(pgrep -f longname).- A PID you saved earlier may now be a different program. Same trap as
psandkill— re-check before you act.
History & Philosophy
lsof was written by Victor A. Abell at Purdue University starting in 1991, and maintained by him for over twenty-five years across dozens of Unix variants (Solaris, AIX, HP-UX, Irix, the BSDs, Linux) — each with a completely different way of exposing open-file state in the kernel. That portability work is why the lsof flag set is so dense: every flag had to do roughly the same thing on every Unix on earth. The project now lives at lsof-org/lsof on GitHub, carried by a small team after Vic retired.
The deeper magic is the one that runs through top, ps, pgrep, and htop: lsof isn't doing anything you can't do yourself. It walks /proc, reads each /proc/<PID>/fd/ directory (every open file descriptor is a symlink there), stats the target, and prints it. That's it. But what it demonstrates by lining them all up — that a TCP connection, a directory, a deleted log file, and a shared library all appear in the same table with the same columns — is the cleanest possible illustration of Unix's central design idea: everything is a file. A socket is a file. A device is a file. A running program's memory is a file. The kernel hands out a small integer for each one and you read and write through it with the same read() and write() calls. lsof is the table view of that universe, and once you've stared at it for a while the box stops being mysterious.
See Also
- ss — the modern socket inspector; faster than
lsof -ifor pure network questions - netstat — the older socket tool
ssreplaced; you'll still see it everywhere - fuser — terser cousin for "who has this file open?"
- ps / top / htop — find the PID before you
lsof -pit - strace —
lsofshows state,straceshows behavior; they're complements - kill — what you do once
lsofnames the culprit - df / du — the disk-full pair, completed by
lsof | grep deleted - pgrep — find PIDs by name; pair with
lsof -p $(pgrep ...) - umount — the command that fails with "device busy" until
lsof +Drescues you - /proc — where every row in
lsofactually comes from - process / file / pipe — the concepts behind every column
- disk full / device busy — the two problems
lsofsolves the fastest
Got an "address already in use" or a disk that's full of nothing?
CleverUptime tracks every listening port and every filesystem on every server, and when one fills up or a port goes silent it tells you in plain language — and points at the exact
lsofone-liner that names the culprit, so you don't have to dig.Want to see your own server's health right now? One command, no signup, no install.