DEV Community

Cover image for I got tired of checking which process was using my ports, so I built Portop
Luca Padovan
Luca Padovan

Posted on AI-assisted

I got tired of checking which process was using my ports, so I built Portop

If you work with Linux long enough, you've probably seen this:

bind: address already in use
Enter fullscreen mode Exit fullscreen mode

And then the usual routine starts.

ss -tlnp
lsof -i :8080
ps ...
systemctl status ...
docker ps ...
Enter fullscreen mode Exit fullscreen mode

These are great tools and I use them all the time.

But on systems with lots of services running, I found myself doing this over and over again just to answer a very simple question:

What is actually using this port?

So I built Portop.

👉 https://github.com/padovanl/portop

Portop is an open source TUI written in Go that gives you a live view of your network ports and tries to connect each socket with the process, service or container behind it.

Portop demo

It started as a tool for myself

I work with Linux systems where having a lot of services and ports in use is normal.

Knowing that port 8080 is listening is useful, but usually that's only the beginning.

I want to know which process owns it.

I want to know the PID and user.

I want to know whether it belongs to a systemd service.

I want to know whether Docker is involved.

And sometimes I just want to kill the process without opening another terminal and running three more commands.

That's basically where Portop came from.

The original version was pretty small.

Then I published it, people started trying it, and the feedback gave me quite a few ideas for things I hadn't originally planned.

What it looks like

The main interface is intentionally simple.

You get a live table of network activity with information about the socket and the process behind it.

Portop can show both listening ports and established connections.

From the same interface you can search and filter entries, inspect a process, open a local web service in your browser, copy information or terminate a process.

Pressing Enter on a process opens more details such as its command line, executable, working directory, user, memory usage, thread count and start time.

There is mouse support too, although I still use it mostly from the keyboard.

Mapping ports back to processes

On Linux, Portop doesn't just parse the output of ss.

It reads the kernel socket tables from:

/proc/net/tcp
/proc/net/tcp6
/proc/net/udp
/proc/net/udp6
Enter fullscreen mode Exit fullscreen mode

It then maps socket inodes back to processes by walking:

/proc/<pid>/fd
Enter fullscreen mode Exit fullscreen mode

From there it can associate the process with additional information.

Systemd and Docker associations are primarily derived from cgroups.

For Docker, container names can also be resolved through read only calls to the Docker Engine Unix socket when available.

There is no privileged background daemon running just to make Portop work.

Of course Linux permissions still apply. If your user cannot inspect another process, Portop cannot magically bypass that. Running it with sudo gives it the additional visibility when needed.

One feature I didn't originally plan: Docker Compose auditing

This one came directly from feedback after I shared the project.

Someone asked whether Portop could look at a Docker Compose project and tell you whether the ports configured there were actually being used.

I liked the idea, so I added it.

You can now run:

portop --compose ./my-stack
Enter fullscreen mode Exit fullscreen mode

Portop reads the published ports from the Compose configuration and compares them with what is actually listening on the machine.

It can identify ports that are behaving as expected, conflicts where another process owns the port, unused ports and dynamic mappings.

When Docker information is available, it can also check whether the listener actually belongs to the expected Compose service.

And because I didn't want this to be useful only interactively, you can combine it with JSON output:

portop --compose ./my-stack --json
Enter fullscreen mode Exit fullscreen mode

Baselines and unexpected ports

Another thing I wanted was a simple way to answer:

What changed?

You can save the current listening ports as a baseline:

portop --save-baseline
Enter fullscreen mode Exit fullscreen mode

Then compare the machine against it later:

portop --diff
Enter fullscreen mode Exit fullscreen mode

This reports ports that appeared or disappeared.

There is also:

portop --watch-new
Enter fullscreen mode Exit fullscreen mode

which watches for newly opened listening ports and can send a desktop notification when something new appears.

Because the diff command can return a nonzero exit code when changes are found, it can also be used from scripts, cron or systemd timers.

Sometimes I don't want a TUI

Portop can output the current state as JSON:

portop --json
Enter fullscreen mode Exit fullscreen mode

So it can also be combined with jq or used as part of another script.

For example, the TUI is useful when I'm investigating something manually, while JSON is much more convenient if I want another tool to consume the same information.

Linux, ARM and macOS

Portop started as a Linux project.

It currently has Linux builds for:

amd64
arm64
armv7
armv6
Enter fullscreen mode Exit fullscreen mode

There are .deb and .rpm packages as well.

More recently I added macOS support for both Intel and Apple Silicon.

The implementation there is necessarily different because macOS doesn't have Linux's /proc filesystem. Portop uses the native lsof and ps tools instead.

Some Linux specific information, such as systemd metadata, obviously isn't available there, but the main Portop workflow still works.

And yes, there are themes

I like TUIs.

So at some point one theme became two, then three, and now Portop has 12 built in themes.

You can switch between them directly from the settings screen without restarting the application.

The same screen lets you remap the keybindings, with changes saved automatically.

This part wasn't exactly necessary to solve the original problem.

But I regret nothing :)

Installing it

The quick installer is:

curl -fsSL https://raw.githubusercontent.com/padovanl/portop/main/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Or with Go:

go install github.com/padovanl/portop/cmd/portop@latest
Enter fullscreen mode Exit fullscreen mode

Then:

portop
Enter fullscreen mode Exit fullscreen mode

You can also start with a filter immediately:

portop 8080
Enter fullscreen mode Exit fullscreen mode

or:

portop redis
Enter fullscreen mode Exit fullscreen mode

Releases are available here:

https://github.com/padovanl/portop/releases

What I want Portop to be

I'm not trying to replace ss, lsof, Wireshark, Docker tooling or the other networking tools that already exist.

They solve much bigger problems than Portop does.

The idea is much simpler:

See a port. Understand what's behind it. Act on it.

Portop started as something I built for myself in my spare time.

Publishing it has been fun because several of the features it has today came from people trying it and saying "this would be useful if..."

So I'm curious where it goes next.

If you work with Linux, containers, servers, embedded devices or just regularly find yourself wondering what stole port 8080, I'd love to hear what you think.

The project is MIT licensed and contributions are welcome.

👉 GitHub: https://github.com/padovanl/portop

If you try it and find a bug, or have an idea for something that would be useful when investigating ports and connections, feel free to open an issue.

Top comments (0)