CLI tool
tiaanduplessis/kill-port avatar
tiaanduplessis/kill-port

kill-port: freeing a busy port from Node and from the shell

❌ Kill the process running on given port

571 stars44 forksJavaScriptMIT

At a glance

What is it?
kill-port is a small MIT licensed npm package that finds the process listening on a port and terminates it, with a JavaScript API and a CLI that accepts single ports, lists and ranges. On Unix it depends on the lsof utility, which is the detail that decides whether it works in your container.
Who is it for?
Adopt kill-port if you need a reliable way to free a port in a development script, a test fixture or a container, and you want one tool that works from JavaScript and from the command line. Skip it if you need to identify the process before killing it, since the package reports output rather than giving you a process handle, and skip it on a minimal Linux image where lsof is absent and cannot be added.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The whole package is a thin wrapper around lsof and a signal

kill-port kills the process running on a given port, and the implementation is more or less what the name suggests. On Unix-like platforms it locates the process holding the port and signals it. The package is small by design, with a main entry at index.js, a cli.js for the command line, index.d.ts for the type declarations, and separate modules for ports and signal handling. The runtime dependencies list is two packages, get-them-args for the CLI argument parsing and shell-exec for running the underlying command, and there is no lsof library because lsof is a system utility rather than a JavaScript one. The declared version is 2.0.1, it is MIT licensed, and the code style is the standard JavaScript style, with the standard package as the linter in the dev dependencies and jest with the node environment for the tests. The repository has test files that match the module layout, with cli.test.js, cli-ranges.test.js, cli-signals.test.js, ports.test.js, signal.test.js and a general test.js, plus a type-tests directory and a separate run script for the type checks, so the type declarations are tested rather than merely shipped.

Using it from Node, and what the promise resolves with

The usage example is a small HTTP server that kills its own port after a second, which is the clearest illustration of the intended use.

js

const kill = require('kill-port')
const http = require('http')
const port = 8080

const server = http.createServer((req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/plain'
  })

  res.end('Hi!')
})

server.listen(port, () => {
  setTimeout(() => {
    
    // Currently you can kill ports running on TCP or UDP protocols
    kill(port, 'tcp')
      .then(console.log)
      .catch(console.log)
  }, 1000)
})

The API surface is one function with three arguments, killPort(port, protocol?, signal?). The port is a number or a decimal integer string including surrounding whitespace, the protocol is case-insensitive tcp or udp and defaults to tcp, and the signal defaults to SIGKILL. Ports must be between 1 and 65535, and the validation happens at runtime rather than at compile time, which is worth knowing if you pass user input. The promise resolves with the final command's result object, where stdout, stderr and cmd are strings, code is 0, and error is absent or undefined. Invalid inputs, missing processes and failed commands reject the promise. There is one caveat stated in the README that deserves to be read twice, that a successful signal request does not guarantee the target process has exited, since the signal is delivered and the process may take a moment to die or may ignore it.

TypeScript support is bundled, and the old types package must go

Type declarations are included with the package, so no separate types package is needed, and the README has a specific instruction for people upgrading from the DefinitelyTyped package: uninstall it.

sh
npm uninstall @types/kill-port

The reason given is that leaving it installed can still cause a shell-exec declaration error, depending on the installed dependency layout, and the bundled declarations have no dependency on shell-exec types or @types/node. That is a real upgrade hazard, since a stale types package in the tree shadows the bundled ones and produces an error that points at the wrong file. The import style is CommonJS by default.

ts
import killPort = require('kill-port')

killPort('3000', 'TCP', 'SIGTERM').then(result => {
  console.log(result.stdout)
})

With esModuleInterop set to true, a default import works as well.

ts
import killPort from 'kill-port'

Note that the protocol argument is passed in upper case in the TypeScript example and the README states the protocol is case-insensitive, so both work, and the signal is case-sensitive, so SIGTERM must be spelled exactly.

The CLI does more than the API, including port ranges

The command line tool is the more capable half. It can be installed globally, and it accepts a port as a positional argument or with a --port flag, with a --method flag for UDP and a --quiet flag to suppress success messages.

sh
$ kill-port --port 8080
# OR
$ kill-port 9000
# OR you can use UDP
$ kill-port 9000 --method udp

Multiple ports are supported as a comma separated list or as several positional arguments, and npx runs it without installing anything.

sh
$ kill-port --port 8080,5000,3000
# OR
$ kill-port 9000 3000 5000

The feature that is not in the original upstream tool is port ranges, with inclusive, ascending ranges accepted in positional arguments or with --port.

sh
$ kill-port 3000-3005
$ kill-port --port 3000-3005
$ kill-port 3000,4000-4002 5000-5001
$ kill-port --quiet --port 3000-3005 --method udp

Every port and range endpoint must be an integer between 1 and 65535, and reversed, empty, malformed and out-of-bounds selections are rejected before any processes are inspected or terminated, with exit status 1. At most 65535 ports including repeated ports may be selected in one invocation, and at most four ports are processed concurrently. That concurrency cap is the one design decision in the CLI that could surprise someone sweeping a large range, since it means a wide selection is processed in batches rather than all at once. Command failures identify the individual port that failed, including within lists and ranges. The JavaScript API, by contrast, continues to accept one port at a time.

Signal selection and the platform limits

The default signal is SIGKILL, which is the blunt choice and explains why the tool works without ceremony, since the process cannot ignore it. When you want a graceful shutdown, the supported signals on Unix-like platforms are SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGKILL and SIGTERM, and the names are case-sensitive. The JavaScript API takes the signal as its third argument.

js
const kill = require('kill-port')
kill(3000, 'tcp', 'SIGINT').then(console.log).cat

The trade is the one the README does not spell out but which follows directly. SIGKILL guarantees the process cannot refuse, at the cost of giving it no chance to close files, flush buffers or release resources, so a server killed that way leaves whatever it had not written unwritten. SIGTERM asks nicely and lets the process clean up, but a process that ignores SIGTERM or is stuck will still be holding the port, and the tool does not escalate. The CLI exposes the signal choice and the API accepts it as an argument, so the choice is yours to make per call. The README also notes that the CLI --quiet flag suppresses success messages only, including verbose success details, while errors remain visible and the exit behaviour is unchanged, which is the behaviour you want in a cleanup script where a failure should still fail the script.

lsof is the dependency that decides whether it works

The Linux and containers section is the most operationally important part of the README, and it is a warning rather than a feature. The Unix implementation requires the full lsof utility on PATH, with support for the -nP and -i flags and its standard network output, and minimal Linux images may omit it. The BusyBox lsof applet uses a different output format and is not a compatible replacement, which is the detail that catches people, since a slim Alpine image often has the BusyBox applet and the tool will then fail in a way that looks like the port being free. The fix is to add the distribution's lsof package to the image, and the README gives the Alpine line.

dockerfile
RUN apk add --no-cache lsof

Installing the utility on the host does not make it available inside a container, and the caller must be able to inspect and signal the target process within its process and network namespaces, with process ownership and permissions limiting what lsof can see. The error handling section is written carefully in the light of that. A missing lsof command or another command failure rejects the API promise, and the CLI reports the command error. The message No process running on port means no matching process was found in the command output, which can also occur with incompatible lsof output or when process visibility is restricted, and the README is careful to say it does not prove the port is unused. The Unix implementation selects TCP listeners on the exact local port, while UDP selection includes connected sockets whose local port matches, and remote ports and established TCP connections without a matching listener are not selected.

Editorial conclusion

Adopt kill-port if you need a reliable way to free a port in a development script, a test fixture or a container, and you want one tool that works from JavaScript and from the command line. Skip it if you need to identify the process before killing it, since the package reports output rather than giving you a process handle, and skip it on a minimal Linux image where lsof is absent and cannot be added. Verify first that lsof is on your PATH with the flags the tool needs, that you accept the default SIGKILL or pass the signal you want, and that a successful kill request actually meant the process exited, since the library does not guarantee that.

Frequently asked questions

How do I kill whatever is using a port with kill-port?

From Node, call killPort(port, protocol?, signal?) with the port as a number or integer string, the protocol tcp or udp defaulting to tcp, and the signal defaulting to SIGKILL. The promise resolves with the final command's stdout, stderr, cmd and code, and rejects on invalid input, a missing process or a failed command.

Does kill-port work in a Docker container?

It works if the full lsof utility is on PATH inside the image, with support for the -nP and -i flags. The BusyBox lsof applet is not a compatible replacement, so minimal images need the distribution lsof package added, and the caller must be able to inspect and signal the target process within its namespaces.

Can the kill-port CLI kill a range of ports?

Yes, inclusive ascending ranges are accepted in positional arguments or with --port, for example kill-port 3000-3005 or kill-port 3000,4000-4002 5000-5001. Every endpoint must be an integer between 1 and 65535, invalid selections are rejected before any process is touched, at most 65535 ports may be selected and at most four are processed concurrently.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. tiaanduplessis/kill-port on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tiaanduplessis-kill-port.svg)](https://hysenlabs.com/projects/tiaanduplessis-kill-port)

Was this useful?