lsof; or: what process is using my port?!
Have you ever gone to start a local development server, only to have it complain that the port is already taken? In this episode you’ll learn about how to use lsof utility to track down exactly which process is hogging a port.
Video transcript & code
So here we are, all ready to write some code, we’re starting up our local app server, da de da…
…and wait, what the heck?
# rails s => Booting WEBrick => Rails 188.8.131.52 application starting in development http://localhost:3000 => Run `rails server --help` for more startup options /usr/local/bundle/gems/actionpack-184.108.40.206/lib/action_dispatch/middleware/stack.rb:37: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call /usr/local/bundle/gems/actionpack-220.127.116.11/lib/action_dispatch/middleware/static.rb:110: warning: The called method `initialize' is defined here [2020-04-17 18:49:41] INFO WEBrick 1.6.0 [2020-04-17 18:49:41] INFO ruby 2.7.0 (2019-12-25) [x86_64-linux] Exiting Traceback (most recent call last): 24: from bin/rails:4:in `<main>' 23: from bin/rails:4:in `require' 22: from /usr/local/bundle/gems/railties-18.104.22.168/lib/rails/commands.rb:18:in `<top (required)>' 21: from /usr/local/bundle/gems/railties-22.214.171.124/lib/rails/command.rb:46:in `invoke' 20: from /usr/local/bundle/gems/railties-126.96.36.199/lib/rails/command/base.rb:69:in `perform' 19: from /usr/local/bundle/gems/thor-1.0.1/lib/thor.rb:392:in `dispatch' 18: from /usr/local/bundle/gems/thor-1.0.1/lib/thor/invocation.rb:127:in `invoke_command' 17: from /usr/local/bundle/gems/thor-1.0.1/lib/thor/command.rb:27:in `run' 16: from /usr/local/bundle/gems/railties-188.8.131.52/lib/rails/commands/server/server_command.rb:138:in `perform' 15: from /usr/local/bundle/gems/railties-184.108.40.206/lib/rails/commands/server/server_command.rb:138:in `tap' 14: from /usr/local/bundle/gems/railties-220.127.116.11/lib/rails/commands/server/server_command.rb:147:in `block in perform' 13: from /usr/local/bundle/gems/railties-18.104.22.168/lib/rails/commands/server/server_command.rb:39:in `start' 12: from /usr/local/bundle/gems/rack-2.2.2/lib/rack/server.rb:327:in `start' 11: from /usr/local/bundle/gems/rack-2.2.2/lib/rack/handler/webrick.rb:38:in `run' 10: from /usr/local/bundle/gems/rack-2.2.2/lib/rack/handler/webrick.rb:38:in `new' 9: from /usr/local/lib/ruby/2.7.0/webrick/httpserver.rb:47:in `initialize' 8: from /usr/local/lib/ruby/2.7.0/webrick/server.rb:108:in `initialize' 7: from /usr/local/lib/ruby/2.7.0/webrick/server.rb:127:in `listen' 6: from /usr/local/lib/ruby/2.7.0/webrick/utils.rb:65:in `create_listeners' 5: from /usr/local/lib/ruby/2.7.0/socket.rb:763:in `tcp_server_sockets' 4: from /usr/local/lib/ruby/2.7.0/socket.rb:227:in `foreach' 3: from /usr/local/lib/ruby/2.7.0/socket.rb:227:in `each' 2: from /usr/local/lib/ruby/2.7.0/socket.rb:765:in `block in tcp_server_sockets' 1: from /usr/local/lib/ruby/2.7.0/socket.rb:201:in `listen' /usr/local/lib/ruby/2.7.0/socket.rb:201:in `bind': Cannot assign requested address - bind(2) for [::1]:3000 (Errno::EADDRNOTAVAIL)
What is all this?!!
If we take a closer look at the error output, we see that the problem is that the dev server is trying to bind to port 3000… but apparently that port is already in use.
1: from /usr/local/lib/ruby/2.7.0/socket.rb:201:in `listen' /usr/local/lib/ruby/2.7.0/socket.rb:201:in `bind': Cannot assign requested address - bind(2) for [::1]:3000 (Errno::EADDRNOTAVAIL)
OK, what do we do now? We need to track down the offending process, and terminate it. But how?
I’ve seen and used a few different techniques for this.
Some people just do a
ps aux and poke through output looking for possible culprits.
# ps aux ...
We can sometimes find the problem process this way, but it’s tedious. What if we could just ask the operating system which process is using port 3000?
As it turns out we can.
But first we need to install a utility that might not already be on our system.
This is an Debian-based linux machine, so I’m using
apt to install it.
$ sudo apt install lsof
lsof utility’s name is short for “LiSt Open Files”.
Let’s try it out!
lsof ...lots of output...
Look at all those open files!
OK, but what does this have to do with finding out which program is using a port??
Well, as it turns out, lsof uses a rather loose definition of “file”. Because it’s written for UNIX-like operating systems where in theory “everything is a file”, lsof also considers I/O resources such as networking sockets to be files.
However, we don’t want to search through the entire output of
lsof any more than we want to search through the output of
Instead, let’s ask
lsof specifically about internet sockets, using the
# lsof -i COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME node 111 root 18u IPv6 24664 0t0 TCP *:43951 (LISTEN) node 111 root 19u IPv6 24332 0t0 TCP localhost:43951->localhost:60576 (ESTABLISHED) node 139 root 18u IPv4 24331 0t0 TCP localhost:60576->localhost:43951 (ESTABLISHED) node 154 root 18u IPv4 24840 0t0 TCP localhost:60578->localhost:43951 (ESTABLISHED) node 186 root 19u IPv6 24338 0t0 TCP localhost:43951->localhost:60578 (ESTABLISHED) http-serv 22752 root 20u IPv4 61898 0t0 TCP *:3000 (LISTEN)
This is a much more manageable list, and we can quickly scan it for the process that’s listening on port 3000.
But there’s an even more precise way to use lsof to find the process responsible for holding onto a port.
Since we know the port number we care about,
we can tell
lsof that’s the only one we’re interested in, by putting
:3000 after the
# lsof -i :3000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME http-serv 22752 root 20u IPv4 61898 0t0 TCP *:3000 (LISTEN)
And there’s our suspect: a process called
…or, is it???
One thing to be mindful of when using
lsof is that by default it truncates longer command names.
We can tell it to show the whole command name with the rather opaquely-named
# lsof -i :3000 +c0 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME http-server 22752 root 20u IPv4 61898 0t0 TCP *:3000 (LISTEN)
If you’re wondering why it’s
+c0… well, for some flags
lsof differentiates between
+ flags to turn an option on, and
- flags to turn it off. The
c options is short for “command”. The flag sets the maximum characters to show, and the special value for putting no limit on it is… zero. Because obviously.
OK, now that we know the full name and process ID of the process that’s getting in our way, we can terminate it with a command like
kill or, in this case,
# pkill http-server
And when we check again… yep, no more process holding onto port 3000.
# lsof -i :3000 +c0
And sure enough, now we can start our development server!
# rails s => Booting WEBrick => Rails 22.214.171.124 application starting in development http://localhost:3000 => Run `rails server --help` for more startup options [2020-04-17 19:49:50] INFO WEBrick 1.6.0 [2020-04-17 19:49:50] INFO ruby 2.7.0 (2019-12-25) [x86_64-linux] [2020-04-17 19:49:50] INFO WEBrick::HTTPServer#start: pid=40515 port=3000
So there you go: next time you have a server complain that it can’t claim the port it wants, you can use
lsof to track down the process currently listening on that port. Happy hacking!