Linux lsof: List Open Files
The `lsof` command (list open files) is an indispensable diagnostic tool for anyone managing Linux systems. At its core, lsof does exactly what its name suggests: it lists all files currently open on...
Key Insights
- lsof reveals all open files on a Linux system, including regular files, network sockets, pipes, and devices—essential for debugging resource locks and port conflicts
- The tool excels at network troubleshooting, instantly showing which process is using a specific port with
lsof -i :PORT - Combining filters with AND (
-a) and OR (comma-separated) operations makes lsof powerful for complex queries across users, processes, and file types
Introduction to lsof
The lsof command (list open files) is an indispensable diagnostic tool for anyone managing Linux systems. At its core, lsof does exactly what its name suggests: it lists all files currently open on your system. But in the Unix philosophy where “everything is a file,” this becomes far more powerful than it initially sounds.
In Linux, regular files are just the beginning. Network sockets, pipes, directories, device files, and even deleted files still held open by processes—all of these are “files” that lsof can track. This makes lsof your go-to tool for answering questions like “why can’t I unmount this filesystem?”, “what process is listening on port 8080?”, or “why is my disk full when du shows plenty of space?”
Here’s what basic lsof output looks like:
$ lsof | head -10
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root cwd DIR 253,0 4096 2 /
systemd 1 root rtd DIR 253,0 4096 2 /
systemd 1 root txt REG 253,0 1632776 1048698 /usr/lib/systemd/systemd
systemd 1 root mem REG 253,0 51856 1048833 /usr/lib/x86_64-linux-gnu/libnss_files-2.31.so
nginx 1234 www 4u IPv4 12345 0t0 TCP *:80 (LISTEN)
nginx 1234 www 5u IPv4 12346 0t0 TCP 192.168.1.10:80->192.168.1.20:54321 (ESTABLISHED)
postgres 5678 db 3u unix 0xffff880123456789 0t0 23456 /var/run/postgresql/.s.PGSQL.5432
Each line shows the command name, process ID, user, file descriptor, file type, and the actual file or connection. This rich output is what makes lsof so versatile.
Basic Usage and Syntax
Running lsof without arguments dumps every open file on the system—typically thousands of entries. This is rarely what you want. Instead, you’ll filter by file, user, or process.
To see which processes have a specific file open:
$ lsof /var/log/nginx/access.log
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 www-data 4w REG 253,0 1048576 1234567 /var/log/nginx/access.log
nginx 1235 www-data 4w REG 253,0 1048576 1234567 /var/log/nginx/access.log
This is invaluable when you encounter “device or resource busy” errors. You can immediately see what’s holding the file open.
To recursively check all files in a directory:
$ lsof +D /var/lib/mysql
Warning: +D performs a full directory tree walk and can be slow on large directories. Use it judiciously.
To see all files opened by a specific user:
$ lsof -u postgres
This shows everything a user has open—useful for auditing or understanding what a compromised account might be accessing. You can also exclude a user with ^:
$ lsof -u ^root # Everything except root's files
Network Connections and Ports
This is where lsof truly shines. Network troubleshooting becomes trivial when you can instantly map ports to processes.
List all network connections:
$ lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
sshd 890 root 3u IPv4 12345 0t0 TCP *:22 (LISTEN)
nginx 1234 www 4u IPv4 12346 0t0 TCP *:80 (LISTEN)
postgres 5678 db 5u IPv4 12347 0t0 TCP localhost:5432 (LISTEN)
chrome 9999 user 42u IPv4 12348 0t0 TCP 192.168.1.10:54321->93.184.216.34:443 (ESTABLISHED)
Find what’s using a specific port—the most common use case:
$ lsof -i :8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 2345 dev 12u IPv4 23456 0t0 TCP *:8080 (LISTEN)
This immediately tells you which process is hogging that port. No more guessing.
Filter by protocol:
$ lsof -i TCP
$ lsof -i UDP
$ lsof -i TCP:1-1024 # Privileged ports only
Check connections to a specific host:
$ lsof -i @93.184.216.34
You can combine port and protocol:
$ lsof -i TCP:443 # All HTTPS connections
Process-Specific Queries
When debugging a specific process, you want to see everything it has open.
Query by process ID:
$ lsof -p 1234
This shows all file descriptors for that PID—regular files, sockets, pipes, everything.
Query by process name:
$ lsof -c nginx
The -c flag matches command names. It accepts partial matches, so lsof -c ssh catches both sshd and ssh.
For scripting, you often just need the PID:
$ lsof -t -i :8080
2345
The -t flag outputs PIDs only, perfect for piping to other commands:
$ kill $(lsof -t -i :8080)
This one-liner kills whatever’s on port 8080. Use with caution.
Advanced Filtering and Combination Options
The real power emerges when combining filters. By default, lsof uses OR logic—it shows results matching any condition. Use -a to require AND logic.
Show network connections for a specific user:
$ lsof -a -u www-data -i
Without -a, this would show all www-data files OR all network files. With -a, you get only network files owned by www-data.
Show files opened by a specific user AND command:
$ lsof -a -u postgres -c postgres
Multiple values for the same option use OR:
$ lsof -u user1,user2 # Files by user1 OR user2
$ lsof -i :80,:443 # Processes on port 80 OR 443
Exclude with ^:
$ lsof -i -u ^root # Network connections not owned by root
Practical Troubleshooting Scenarios
Scenario 1: Can’t delete or unmount a file/directory
$ rm large_file.dat
rm: cannot remove 'large_file.dat': Device or resource busy
$ lsof large_file.dat
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
backup 3456 root 5r REG 253,0 5242880 7890 large_file.dat
Now you know process 3456 has it open. Check if it’s safe to kill or wait for it to finish.
Scenario 2: Port conflict on application startup
$ ./myapp
Error: Address already in use (port 3000)
$ lsof -i :3000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 4567 dev 10u IPv4 45678 0t0 TCP *:3000 (LISTEN)
You find a stale node process. Kill it and restart your app.
Scenario 3: Disk space mysteriously consumed
Sometimes processes hold deleted files open, preventing space reclamation:
$ lsof | grep deleted
apache 1234 www 5w REG 253,0 10737418240 12345 /var/log/apache/access.log (deleted)
That’s 10GB held by a deleted log file. Restart apache or truncate the file descriptor to reclaim space.
Scenario 4: Finding loaded libraries
$ lsof -p 1234 | grep '\.so'
This shows all shared libraries loaded by process 1234—useful for debugging library version issues.
Output Formatting and Scripting
For scripts, lsof’s default output is hard to parse. Use -F for field-separated output:
$ lsof -F pcn -i :80
p1234
cnginx
p1235
cnginx
Each field is prefixed: p for PID, c for command, n for name. This is parseable but ugly. For most scripts, combining with standard tools works better:
$ lsof -i :80 | awk 'NR>1 {print $2}' | sort -u
1234
1235
This extracts unique PIDs from column 2, skipping the header.
Get process names using a specific file:
$ lsof /var/log/app.log | awk 'NR>1 {print $1}' | sort -u
One-liner to free a port by killing its process:
$ lsof -ti :8080 | xargs kill -9
The -t gives just PIDs, xargs passes them to kill. The -i option ensures you only get network-related PIDs.
Check if any process has files open in a directory before unmounting:
$ if lsof +D /mnt/data > /dev/null 2>&1; then
echo "Files still open, cannot unmount"
else
umount /mnt/data
fi
Monitor network connections in real-time:
$ watch -n 1 'lsof -i -P -n | grep ESTABLISHED'
The -P prevents port name lookup, -n prevents hostname resolution—both speed up output significantly.
Conclusion
Master lsof and you’ll solve problems in seconds that would otherwise take hours of guessing. The key is understanding that in Linux, everything really is a file—and lsof gives you complete visibility into what’s open, who opened it, and how it’s being used. Keep it in your troubleshooting toolkit alongside netstat, ss, and ps. When mysterious errors strike, lsof usually has the answer.