Show HN: Killport – CLI tool to kill processes running on a specified port
github.com
github.com
fuser -k 8080/tcp # kill processes with TCP :8080 open, IPv4 and IPv6
fuser -k -6 8080/tcp # same but IPv6 only
fuser -k 8080/udp # UDP instead of TCP
[0] https://gitlab.com/psmisc/psmiscYou can also use it to kill processes that have a file open too, which is also handy if your hung server is also hanging onto files as well as ports.
lsof -P | grep LISTEN | grep $PORT
kill $RELEVANT_PID
The rust script itself uses kill -9, which probably isn't the ideal behavior. SIGTERM and then SIGKILL after a timeout is probably the appropriate behavior for a script, but even more appropriate would be to figure out if it's supervised and `service stop` it.The real problem with this script is that it violates the unix philosophy (https://en.wikipedia.org/wiki/Unix_philosophy). "Do one thing and do it well" is the main problem.
This program finds processes to kill AND it kills them AND it kills their children and it does each of those things poorly.
For this program to be better than `lsof` and `kill`, it would need to implement command line options for which type of signal to send. By the time that is implemented, a program with the same interface as kill will be implemented, and it would be better for that to be it's own program that could be piped too. What's left would be a program that finds a process listening to a specific port, and there are already plenty of good programs for that.
If you have a "cargo cult" culture rather than an "understand the problem" culture what happens is that you hire a new dev and teach them that when the lid closes, you run killport to get it running again. The environment takes 10 seconds to set up and that's fine. By the time you are 100 employees you have 100 employees killporting their dev server and now taking 2 minutes to restart.
If you're lucky you get an altruistic hero dev who says "screw this, I don't want to deal with this any more" and they solve it. If you're unlucky, the behavior gets scaled out to 1000 employees your dev server takes 10 minutes to initialize and your developer tools team is under resourced and has higher priority stuff to deal with since "dev server hung" already has a solution. That's a little hyperbolic, but I've seen similar things.
Maybe what you really want is a cron job or supervisor that health checks your environment and restarts it when it's hung. Maybe there's a place to shim open/close lid behavior. Maybe spinning disks are being used instead of SSD's. Maybe the problem is local dev environment instead of remote. Most of those suggestions are automations or mitigations. The real solution might be as simple as a configuration change.
It seems like a very solvable problem.
> now cant boot my dev server during local development
Why?
Because you can't have two processes using the same port, and the process that was using the port is now not responding. Perhaps you don't even want to go through the process of learning what PID it has, you just want the port back so you can relaunch the dev server on localhost.
your criticisms of this program do not seem well-founded because your assuming that it will be used improperly. In many cases this is a very simple problem with a simple solution
Yes, but why?
Between strace, ps, atop, lsof, logging, and understanding the OS and TCP, it shouldn't be horrific to figure out what is happening. The very same skills to understand the problem are valuable to a lot of problems and investing in one simple problem is a further investment in future problems. "I don't have the telemetry to understand this" is, if nothing else, a deferred problem. Having devs that aren't prepared for oncall is also a problem.
"Why is my process hung?" is something I hope most devs can solve.
Not solving a frequently hanging process is short-termism. Not solving problems creates a learned helplessness about systemic problems. They build up and get addressed too late.
> Because you can't have two processes using the same port
https://lwn.net/Articles/542629/
Maybe this is relevant: https://hea-www.harvard.edu/~fine/Tech/addrinuse.html
> criticisms of this program
I am a little harsh because it increases complexity when no increased complexity is needed. That is my only strong criticism, a whole new program is invoked when we have plenty of very sufficient (composable) tools already.
$ lsof -i:$port | grep LISTEN
$ kill $pid lsof -s TCP:LISTEN -i :$portnetstat -nap | grep port (to see the proc and get its pid)
and then kill -9 pid to kill it
This is for development environment: you know that tool foo always runs on port 8888; you want a clean slate before rerunning your tests/dev server/ whatever.
(But sure, personally I would say people should learn enough shell programming to use shell commands for this, or even make their own utility as a shell script/function, rather than use a one-off tool).
kill $(lsof -t -i:8080)For many people, it’s faster to write an entirely new tool than it is to go learn the platform for which they’re writing and therefore they do it.
The similar thing also regularly happens in medium-to-large codebases so that's how you end with four slighlty different implementations of e.g. "extract value by path from a JSON object, with some traversal options": it's easier to just write the damn loop by yourself than to find a) if it's already been written, and if yes, b) how the hell do you use it.
I think it says more about people being lazy and not willing to use that mighty tool called search (add ChatGPT now). Even here on HN I often see people asking what is X when that X is one web search away.
On a serious note, I do hope that built-in help systems with ChatGPT glued on (and additionally trained on its contents) will become a de-facto standard in the near future: it should be possible to just ask your computing environment "how do I do X with you?" and get an answer or at least some guidance pointers.
I've personally written what is very comprehensive doc for one of my software products. It described every nook and cranny. Still I ended up supporting a forum with most common questions percolated to a dedicated How Do I Do XYZ section and it just keeps growing. In the beginning I was answering question there. Now I just see people supporting themselves.
Meh. Back in ye olde days, when developers were either self-taught from the roots or came from universities, they just knew that stuff from experience.
Nowadays, a lot of "developers" come from three months "coder bootcamps". They may even be reasonably proficient in whatever flavor of JS toolkit the camps teach at the moment, but they will have zero knowledge about what makes their computer tick.
How would I use man to find that lsof exists so that I could come up with the incantation mentioned in OP? I tried "man -k 'open'" but none of the answers returns lsof on my system. I checked that "man lsof" works as expected.
man -k open
should surface lsof on your system. On mine it only has the description 'open files', though, which might not be obviously relevant to many people.I'm not sure what macOS or FreeBSD `man` have, but GNU `man` has `-K` as well as `-k`. The uppercase version searches the manpages globally, instead of just their short descriptions. So something like
man -K -a 8 "list open" port
will turn up `lsof` on GNU, even if `apropos` doesn't.If you have a tldr client installed, that can also be useful for searching through common examples including for programs you may not have installed. For example with tealdeer, the following (Fish) surfaces both lsof and netstat as options:
for page in (tldr -l)
tldr $page | rg -A2 -i '\b(find|list)\b.*\bport'
end
and for page in (tldr -l)
tldr $page | rg -A2 -i '\bkill\b.*\bport'
end
shows that fuser and fkill can both do the same thing as killport.Grep sucks as a search interface for tldr, though, so you probably want a better tldr client than I have, which will do global search, or an FZF script that will let you filter through tldr results.
Discovering new (to you) tools which are not yet installed is a good reason to use the web to look up Unix stuff, especially after you've checked the manpages and your package manager.
> Discovering new (to you) tools which are not yet installed is a good reason to use the web to look up Unix stuff
Of course a web search can help, however I often find that the times when I need to do such searches is quite correlated with times when I don't have internet access. Less frequent now with my "smart" phone, but still happens.
People that unwilling to learn— uninterested in even discovering that there is a manual and how to open it— don't belong in software.
The rest of the things you mention... whatever. Many people haven't heard a good pitch. They haven't had the relevant experience to see how investing in learning those things can pay off for them. But disinterest in finding out that there's a manual? That's wilful incompetence of a kind I've never seen and hope I never will.
P.s: functions are almost always preferable over aliases
But I agree with it, because aliases are just a string substitution for interactive shells, so they are limited. I found that I often end up wanting to add additional logic (such as arguments or local variables) and to have it available for use in scripts later.
[0] https://www.gnu.org/software/bash/manual/html_node/Aliases.h...
Relevant xkcd: https://xkcd.com/1168/
I may not come up with that single liner when I need but `lsof -i` (i for internet) is ok for me to resolve some issue. Err, `sudo lsof -i`
At least with Rust it can be abstracted and made to be portable, to work on other platforms like Windows.
Why learn 3 different commands with slightly non-standard arguments to find and kill a process on different OSes, when you can use one unified command and the arguments work the same as expected?
This is why most people (and developers) still use the Windows Desktop and not the thousands of Linux Desktop(s) out there.
> But no other OSes exist (that are worth to get oneself acquainted with)
macOS exists and it works with that. Rust supports Windows as well so it is certainly possible to port it to Windows. Literally someone asked for Windows. [0]
> I am pretty sure of it.
Henceforth, I don't think you are even sure.
your parent poster is obviously trolling, but macOS is some kind of BSD, so they actually took this case in account.
I'm failing to see the logic here. As a Linux user, trust me there are tons of people out there for which macOS is the only OS that exists or matters. It doesn't prevent people from switching to macOS. Years back that was true of nearly every Windows user as well (only Windows existed). I'm not understanding the causative chain here.
lsof isn't something you automatically pickup if it's not your daily business (like so many other things). I for myself often forget about it but I have some usual ports to kill. So these days I simply search my shell history by port to find lsof again.
Its a good thing now we have chatGpt to help us :)
What we really need is a tool like this for Windows. Find the process that keeps this file open and kill it. It's so annoying when I can't delete a file because some process has it open but I can't tell which one.
https://learn.microsoft.com/en-us/sysinternals/downloads/han...
https://learn.microsoft.com/en-us/windows/powertoys/file-loc...
Nevermind Windows, I want an app like this for android.
Back in the DOS era, there were few enough files on the system that I could just explore around and learn what everything did. That's no longer practical, but what's the alternative? There doesn't seem to be a "top 100 commands to get familiar with", or whatever. The ss64.com pages are pretty great, but...
Choose a random entry you don't recognise and open the man page for it. There's not that many on most systems - I mean, there's likely a couple hundred or more, but some are convenience symlinks and many you'll already know.
For things you may not have by default, there's a few "awesome" lists available, like https://github.com/agarrharr/awesome-cli-apps
The problem is less that people don't understand shell commands but more there is too much stuff to remember.
An alias works well for this stuff but then you have to set it up everywhere you work. Sometimes downloading a package is just easier.
npx kill-port 8080
But is it really better in the long run to add more and more additional tools?
Is it less work to understand and remember more uses of fewer commands or is it less work to understand and remember more specialized commands? The latter is clearly more administrative work, because you have to install all those tools on your machines. On the other hand, finding more uses of a few select tools creates those associations in your brain that you need to cover new usages for them yourself.
To me that's a clear win for the side of fewer well-understood tools.
And as they said, 'which you can easily turn into an alias [well, a function]', which you could call 'killport' if you want for exactly the same functionality.
And besides having to learn that lsof exists, you'd also have to learn that this even more obscure tool exists. And what happens if you prefer for the program to terminate itself gracefully, cleaning up whatever mess it might have made, instead of sending SIGKILL? Now you have to look up the PID or program name anyway, probably using lsof..
Don't look basic Unix stuff up on the web. Look it up in your Unix environment. The documentation is self-contained, and accessing it internally is way, way more efficient.
So instead, try one of these:
- use `man lsof`, search 'EXAMPLES' to jump down to the examples section, and search for the word 'port'
- search directly though examples: `tldr lsof | grep -A2 -i port`
- look through your shell history with grep or CTRL+R
- type the first few letters of the command and let history-based autocompletion hint the rest to you
Those options may seem strange if you're not used to them. But if you're not using techniques like that, and instead going to your web browser to look up CLI usage, you don't need to learn the CLI— you need to learn how to learn the CLI. Once you do that, a ton of things will become much easier and quicker for you to deal with. sudo lsof -t -i :22
I expected to get sshd and nothing else, but it actually includes outgoing ssh connections too - which I don't want to kill. Likewise :443 gives clients like the pids of firefox and signal. I'm gonna stick with `fuser`.That is the full browser instance not just the specific tab connection to the server
This is the command I would use.
> kill -9 (lsof -nP -t -iTCP:8080 -sTCP:LISTEN)
Especially since it looks like it's reading the owning process's cmdline per-fd.
I phrased it poorly. Doing the loop is what it is, but doing the loop and allocating inside (which 'process.cmdline()' does) on every loop is something I'm fairly sure none of the other tools do.
https://github.com/anisse/tcpkill/blob/cfd96d5dec438a3722edb...
This one doesn't either. The code structure could be clearer IMHO, but `kill_processes_by_inode` reads the cmdline within the `if target_inode == inode` block, which breaks out of the `for fd in fds` loop at the end. So it only looks at the cmdline once per process that has the target inode.
That said, if `find_target_inodes` returns n inodes, `kill_processes_by_port` will call `kill_processes_by_inode` n times. It'd be better to find all fds only once and compare each to all the target inodes at once with a hash set (if n might be large) or by bisecting a sorted slice. Multiple inodes per port could happen in a few different ways: different processes listening to the same port on different IPv4/IPv6 addresses, an old-fashioned pre-forked sort of server model, a bunch of individually spawned single-threaded servers listening on the same port via `SO_REUSEPORT`/`SO_REUSEADDR`.
Any implementation of this objective has the same limitation, though.
> curl -sL https://bit.ly/killport | sh
let processes = procfs::process::all_processes().unwrap();
...
let process = p.unwrap();
Doesn't this use of unwrap cancel out one of the advantages of Rust which is to handle errors quite comprehensively?Here process could be null, and we try to access it the line after.
edit: actually process won't be null, unwrap will panic. Got it. I still think a good error message would be better than exposing the user to a crash, but that's a matter of opinion. It seems like the whole point of using Rust, and also of such a tool, is to be robust and user-friendly (since it implements an UNIX one liner), and an unhandled crash could lower the confidence of the user in the tool.
[1] https://github.com/jkfran/killport/blob/3a43d037c08ab3aec730...
[2] https://github.com/jkfran/killport/blob/3a43d037c08ab3aec730...
The only downside is that the error message won't look great, but for a tool like this, that seems fine.
> Here process could be null, and we try to access it the line after.
Process cannot be null. Rust does not have nulls outside of unsafe code.
The type of 'p' there is 'Result<Process, ProcError>', and so 'process' is of type 'Process', which cant' be null.
That's one of the actual benefits of rust: it is memory safe outside of unsafe code, and doesn't make the "null" mistake.
No, you list what you can and give meaningful error message for any particular offenders.
Also, as another commenter mentioned, the `let process = p.unwrap()` is suspicious. I imagine this can happen if a process simply exits between being returned from the `/proc` traversal and `opendir("/proc/<pid>")`. If the error is `ENOENT`, it should simply continue to the next pid without printing an error at all.
But seriously kids, take the time to learn Unix tools so you can save yourself from needing to write hundreds of lines of Rust when one shell command will do the same thing.
Maybe we should patch kill itself? It wouldn't surprise me one bit if that pull request exists.
If, for example, you run nginx in a default configuration, you'll notice both the master and worker processes will have an fd bound to the same port (port 80 or whatever), so it's also not exactly unusual.
Even without SO_REUSEPORT, you can have multiple things on one port if you have multiple IPs, like something listening on "127.0.0.1:80", and a different process listening on "192.168.1.10:80"
sshd 3891262 root 4u IPv4 15450342 0t0 TCP ibpmaas-testing:https->10.10.47.11:49064 (ESTABLISHED)
sshd 3891351 ubuntu 4u IPv4 15450342 0t0 TCP ibpmaas-testing:https->10.10.47.11:49064 (ESTABLISHED)
# lsof -ti ":49064"
3891262 3891351
edit for readability
kill $(lsof -t -i:TCP:$1 -sTCP:LISTEN)
will do just fine. But you probably want listening ports only, hence -s.Unless you want it to work with millions of listening processes, which would overflow your environment and require the use of xargs.
In that case you probably want GNU xargs with -n and -p to batch the job and run processes in parallel because that's going to take a while.
alias portkill='function killit(){kill -9 "$(lsof -t -i:$1)" &>/dev/null || true};killit'
then run portkill PORT
Just add it to your shell config (e.g. `.zshrc`) and use it like so: `$ killport 8080`
It has two implemented methods: one uses pidfd, the other uses netlink INET_DIAG_DESTROY (like ss -K).
CLI UX could be improved, but it does the job.
ss -tunlp '(dst = :8080 or src = :8080)'
ss -K '(dport = 8080 or sport = 8080)'
BSD has fstat(1) which will list open file descriptors including network connections.NetBSD has sockstat(1) which lists open sockets.
OpenBSD has tcpdrop(1) but that's only TCP.
MacOS has none of these, apparently. It looks like it comes with fuser and lsof instead.
ip -all netns exec ss -K …https://github.com/helpermethod/pk/blob/main/pk
Also handles port reuse
If it's "npx killport", it seems to not work on Windows.
kill $(lsof -t -i :PORT_NUMBER) kill -9 "$(lsof -t -i:5000)" &>/dev/null || true(On my laptop, this kills stern, Chrome, Slack, Firefox, and 1Pass…)
John is not walking to the game.