On Unix, everything is a file — sockets, pipes, device nodes, and actual files on disk. lsof (list open files) gives you a live view of every file descriptor held open by every process on your system. It’s the first tool to reach for when a port is already in use, a file won’t delete because something holds it open, or you want to know exactly what a process is doing.

Basic usage

$ lsof
COMMAND     PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd       1   root  cwd    DIR    8,1     4096    2 /
sshd       1234   root    4u  IPv4  12345      0t0  TCP *:22 (LISTEN)
nginx      5678   root    6u  IPv4  23456      0t0  TCP *:80 (LISTEN)
python3    9012  mukul    3u  IPv4  34567      0t0  TCP 127.0.0.1:5000 (LISTEN)
...

The output is verbose — typically thousands of lines. You’ll almost always pair lsof with a filter.

Finding what’s using a port

This is the most common reason to reach for lsof:

$ lsof -i :8080
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node     4321  mukul   23u  IPv4  56789      0t0  TCP *:http-alt (LISTEN)

-i filters by network file. :8080 matches any process listening on port 8080. Now you have the PID and can kill it:

$ kill 4321
# or forcefully:
$ kill -9 4321

Filter by protocol or address

$ lsof -i tcp            # all TCP connections
$ lsof -i udp:53         # DNS (UDP port 53)
$ lsof -i @192.168.1.10  # connections to a specific host
$ lsof -i tcp:3000-3100  # port range

Listing open files for a specific process

$ lsof -p 1234

Or by process name:

$ lsof -c nginx

-c matches processes whose name starts with the string. Combine with -r to watch in real time:

$ lsof -c nginx -r 2   # refresh every 2 seconds

Finding which process has a file open

Useful when you can’t delete a file because something still holds it:

$ lsof /var/log/app.log
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
myapp   8888  mukul    1w   REG    8,1   204800  123 /var/log/app.log

Same trick for a whole directory:

$ lsof +D /var/log/

+D recurses into subdirectories. It can be slow on large trees.

Listing all network connections

$ lsof -i
COMMAND     PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd       1234   root    3u  IPv4  11111      0t0  TCP *:22 (LISTEN)
postgres   2345 postgres   5u  IPv4  22222      0t0  TCP 127.0.0.1:5432 (LISTEN)
chrome    56789  mukul   45u  IPv4  33333      0t0  TCP 192.168.1.5:51234->142.250.1.1:443 (ESTABLISHED)

To show only established connections (no listeners):

$ lsof -i -s TCP:ESTABLISHED

To show only listeners:

$ lsof -i -s TCP:LISTEN

Listing deleted files still held open

A common gotcha: a log file is deleted, but disk space isn’t freed because a process still has it open. lsof shows these as (deleted):

$ lsof | grep deleted
nginx   4567  root    2w   REG    8,1   1073741824  999 /var/log/nginx/error.log (deleted)

You can recover the content from /proc/<pid>/fd/<fd> or just restart the process to release the handle and reclaim the space.

Useful flags cheat sheet

Flag Meaning
-i :PORT Files on network port
-p PID Files for a specific PID
-c NAME Files for processes matching name
-u USER Files opened by a user
+D DIR All open files under a directory
-t Output PIDs only (pipe-friendly)
-n Skip hostname resolution (faster)
-P Skip port-name resolution (shows numbers)
-r N Repeat every N seconds

-n and -P speed things up significantly

DNS and port-name lookups make lsof sluggish. Add both flags when you don’t need human-readable names:

$ lsof -nP -i :5432
COMMAND     PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
postgres   2345 postgres    5u  IPv4  22222      0t0  TCP 127.0.0.1:5432 (LISTEN)

Pipe-friendly output with -t

-t outputs only the PID, making it easy to feed into kill:

# Kill everything on port 3000
$ kill $(lsof -t -i :3000)

macOS quirks

On macOS, lsof is preinstalled and works the same way. One difference: macOS uses launchd for many system sockets, so some entries will show launchd or com.apple.* names. Also, some system sockets may require sudo to show up:

$ sudo lsof -i :443

Conclusion

lsof is the definitive answer to “what has this port/file open?” Once you know the core flags — -i for network, -p for PID, -c for process name, -t for pipe-friendly output — you can diagnose port conflicts, track down file handle leaks, and understand exactly what a running process is touching. Adding -nP as a habit keeps it fast. It’s one of those tools you install once and never uninstall.