lsof stands for “list open files,” and honestly, it should be in your Linux starter pack right next to grep and curl. Most sysadmins barely scratch the surface, using it only to find what’s listening on a port. But the tool does way more than that. It’s like having X-ray vision into what your system is actually doing.
Here’s the thing: on Linux, everything is a file. Devices. Network sockets. Pipes. Regular files. When something goes wrong, lsof shows you the evidence. Let me show you the patterns that’ll save you hours of debugging.
The Basics: What Is lsof Doing?
lsof reads the /proc filesystem and shows you every file descriptor open by every process. It’s simple in concept, powerful in practice.
$ lsof -p 1234COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEmyapp 1234 root cwd DIR 10,0 4096 256 /home/appmyapp 1234 root rtd DIR 10,0 4096 2 /myapp 1234 root txt REG 10,0 2097152 1048576 /usr/bin/myappmyapp 1234 root 0u CHR 1,3 0 6 /dev/nullmyapp 1234 root 1w REG 10,0 50000 2000000 /var/log/app.logEach row is one file descriptor. The columns tell you what process owns it, what the file/socket/device is, and where it lives.
Pattern 1: Find What’s Using a Port
This is the go-to use case. Port 8080 is already listening, and you have no idea why.
$ sudo lsof -i :8080COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEpython 2341 user 3u IPv4 34567 0t0 TCP *:8080 (LISTEN)There it is. Process ID 2341, running Python, listening on 8080.
If you need to be more specific:
# Only TCP (not UDP)$ sudo lsof -i TCP:8080
# Only listening sockets (not established connections)$ sudo lsof -i :8080 -sTCP:LISTEN
# Find all connections to a specific host$ lsof -i @192.168.1.100Pattern 2: Find Files a Process Has Open
Your app is running out of disk space, but du shows nothing obvious. Maybe it’s got a deleted file open that’s still consuming space.
$ lsof -p 5678 | grep deletedmyapp 5678 root 15w REG 10,0 1073741824 5000000 /var/log/old.log (deleted)There it is. A 1 GB file that was deleted but still open. The inode isn’t cleaned up because the process still has a file handle to it. Kill the process or send it a signal to rotate logs, and the space is freed.
Or just list everything a process has open:
$ lsof -p 5678COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEmyapp 5678 root cwd DIR 10,0 4096 256 /home/appmyapp 5678 root 0u CHR 1,3 0 6 /dev/nullmyapp 5678 root 1w REG 10,0 50000 2000000 /var/log/app.logmyapp 5678 root 5u REG 10,0 100 998765 /etc/config.yaml...Pattern 3: See All Network Connections
No netstat? No problem.
$ lsof -iCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEsshd 1234 root 3u IPv4 12345 0t0 TCP myhost:ssh->remote:54321 (ESTABLISHED)nginx 5678 root 6u IPv4 12346 0t0 TCP *:http (LISTEN)curl 90210 user 3u IPv4 12347 0t0 TCP 192.168.1.5:54321->example.com:443 (ESTABLISHED)Established connections, listening sockets, pending connections — all in one view.
Pattern 4: Find Which Process Is Holding a File
You’re trying to unmount a filesystem, but it says “device busy.” Something has that directory open.
$ lsof /mnt/dataCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEbash 2341 user cwd DIR 15,2 4096 131072 /mnt/datapython 5678 user 5r REG 15,2 50000 65536 /mnt/data/config.yamlProcess 2341 (your shell) has /mnt/data as its current directory. Process 5678 has a file open in it. Kill or redirect them, then the unmount will work.
Pattern 5: See What a Running Service Owns
For services running under their own user:
$ lsof -u postgresCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEpostgres 1234 postgres cwd DIR 10,0 4096 256 /var/lib/postgresqlpostgres 1234 postgres 0r CHR 1,3 0 6 /dev/nullpostgres 1234 postgres 4u REG 10,0 536870912 9999999 /var/lib/postgresql/data/base/1...This shows everything the postgres user has open. Useful for diagnostics without having to track a specific PID.
Pattern 6: Recursive Directory Searches
Who has files open under /home/?
$ lsof +D /home/COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEbash 2341 user cwd DIR 10,0 4096 131072 /home/userpython 5678 user 5r REG 10,0 50000 65536 /home/user/script.pyvi 9999 user 1w REG 10,0 0 999999 /home/user/.temp.swpThis recursively walks the directory tree. Slower than specific searches, but powerful for investigation.
The Real-World Scenarios
Debugging port conflicts at startup:
sudo lsof -i :3000 && echo "Port already in use" || echo "Port is free"Finding zombie processes holding locks:
lsof /var/lock/myapp.lockMonitoring disk space usage by deleted files:
sudo lsof | grep deleted | awk '{print $7}' | awk '{sum+=$1} END {print sum " bytes"}'See all open connections to a database:
lsof -i @db.internal.localWhy This Matters
lsof is your window into what Linux is actually doing. Port conflicts, disk space mysteries, hung processes, zombie files — lsof shows the evidence. It’s the first tool I reach for when things aren’t making sense, and it beats guessing every time.
Learn the -p, -i, and +D patterns, and you’ll solve 90% of runtime mysteries. The rest is just reading the output carefully.