The fastest way to free a busy development port on macOS.
PortMedic is a lightweight, native macOS utility that lists every process
listening on a network port and lets you terminate it with a single click.
It replaces the repetitive lsof -i :8080 / kill -9 <PID> terminal workflow
that developers run countless times a week.
Local development constantly leaves ports occupied by crashed or orphaned processes:
Address already in use — port 8080 is already occupied.
Resolving this normally means leaving your editor, finding the process, copying its PID, killing it, and switching back. PortMedic collapses that into one click, without opening a terminal or a heavyweight IDE plugin.
- Lists all active listening ports with their PID, process name and protocol.
- One-click termination with a confirmation prompt.
- Search across port, PID, process name, user and detected framework.
- Automatic framework detection, so a port shows "PostgreSQL" or "Next.js"
instead of a generic
dockerornode. - Process detail panel with executable path, working directory, user and protocol.
- Menu bar extra for checking and refreshing ports without opening the window.
- Configurable auto-refresh interval and launch-at-login.
PortMedic recognises common development services by port and process name:
| Detected | Matched by |
|---|---|
| Next.js / Node.js | node process (port 3000) |
| Vite Dev Server | port 5173 |
| Spring Boot | java process |
| PostgreSQL | port 5432 |
| MySQL | port 3306 |
| Redis | port 6379 |
| MongoDB | port 27017 |
| Docker | docker process |
| Python | python process |
Port matches take precedence over process-name matches, so a Postgres
container reporting as docker is still labelled "PostgreSQL" and is
findable by searching postgres.
PortMedic is built to stay out of the way. Measured on a release build:
| Metric | Value |
|---|---|
| Application bundle | 3.04 MB |
| Resident memory | ~80 MB |
| CPU while idle | 0.0% |
There is no bundled runtime, no embedded browser and no background daemon. Polling is suspended automatically whenever PortMedic is not the active application, scan results are only republished when something actually changed, and framework detection is resolved once per scan rather than on every redraw. The result is a utility that costs effectively nothing to leave running all day.
- macOS 15.5 or later
- Xcode 16.4 or later (to build from source)
An experimental Rust port lives in portmedic-linux/.
It includes the dashboard, process controls, watched ports, quick actions,
system tray integration, and Linux desktop branding. See its README for
build, desktop registration, and packaging details.
Download PortMedic-X.Y.Z.dmg from the Releases page,
open it, and drag PortMedic.app into Applications.
Release builds are ad-hoc signed (not by an Apple Developer ID) and not notarised, since that requires a paid Apple Developer Program membership. On first launch macOS Gatekeeper may still refuse to open the app with an "Apple could not verify..." dialog that only offers Done or Move to Trash, with no "Open Anyway" button. To open it anyway:
- Try double-clicking it once (it will be blocked), then go to System Settings > Privacy & Security, scroll to the Security section, and click Open Anyway next to the PortMedic message, or
- Run
xattr -cr /Applications/PortMedic.appin Terminal to clear the quarantine flag, which lets it open normally without any prompt.
git clone https://github.com/bansikah22/PortMedic.git
cd PortMedic
open PortMedic.xcodeprojSelect the PortMedic scheme and press Run, or build from the command line:
xcodebuild -project PortMedic.xcodeproj -scheme PortMedic buildLinux releases are published as a portable tar.gz on the Releases
page. Extract the archive,
build no additional Rust code, and register the application with:
tar -xzf PortMedic-linux-X.Y.Z.tar.gz
cd PortMedic-linux-X.Y.Z
./install.sh
./portmedic-linuxLinux release tags use the linux-vX.Y.Z format so they remain separate from
the macOS release tags.
The archive includes the release binary, PortMedic icon, desktop entry, and installer. Linux desktop libraries such as GTK3 and X11/Wayland support must already be available on the system; see the Linux README for package names.
- Launch PortMedic. The dashboard lists every listening port.
- Search by port, PID, process name or framework.
- Click Kill on a row and confirm to release the port.
- Select a row to inspect the process's executable path and working directory.
The status bar shows the number of active ports and the last refresh time. Auto-refresh runs every 5 seconds by default and is configurable in Settings.
PortMedic runs unsandboxed because it must inspect and signal processes owned
by other applications. It sends SIGKILL by default, matching kill -9.
Some processes cannot be freed by signalling alone:
- Services supervised by another process (for example a container managed by Docker Desktop) are restarted automatically after being killed.
- Processes owned by another user or by the system require elevated privileges.
In both cases PortMedic reports that the port is still in use rather than failing silently.
Every service is defined as a protocol with a concrete implementation and is
injected through the view model's initialiser. This keeps view models testable
without spawning subprocesses or sending real signals. Pure logic such as
LsofOutputParser is deliberately separated from process invocation so it can
be unit tested directly.
Run the test suite:
xcodebuild test -project PortMedic.xcodeproj -scheme PortMedicTo exercise the scan and kill flow without a real development stack, start a set of dummy listeners on common ports:
./scripts/start-test-listeners.sh # opens ports 3000, 5173, 5432, 6379, 27017, 8080
./scripts/stop-test-listeners.sh # cleans them upNote that rebuilding does not restart an already-running instance. Quit PortMedic before rebuilding, otherwise you will be testing a stale binary.
Lint locally with the same rules CI enforces:
brew install swiftlint
swiftlint lint --strictEvery push and pull request against main runs the CI workflow, which
builds the app, runs the test suite with code coverage, uploads the resulting
.xcresult bundle as an artifact, and lints the codebase with SwiftLint in
strict mode.
Tagging a commit as vX.Y.Z runs the Release workflow, which builds a
Release configuration, packages PortMedic.app as a .dmg disk image with a
SHA-256 checksum, and opens a draft GitHub release. Release builds are
currently unsigned; distributing a signed and notarised build requires a
Developer ID certificate stored in repository secrets.
Contributions are welcome. Please:
- Open an issue describing the change before starting significant work.
- Follow the existing MVVM structure and inject new services via protocols.
- Add unit tests for new logic and keep the suite green.
- Run
swiftlint lint --strictand keep the build and tests green. - Keep commits focused and write descriptive commit messages.
Released under the MIT License. See LICENSE for details.

