CLI tool
amak-tech/port-buddy avatar
amak-tech/port-buddy

Port Buddy tunnels TCP and UDP as well as HTTP, which is where it differs from ngrok

Expose local ports to the internet in seconds

537 stars46 forksJavaApache-2.0

At a glance

What is it?
Port Buddy is a Java and GraalVM tunneling service that exposes local ports over HTTP, raw TCP and UDP with a single command, backed by a multi-module Spring Boot stack with its own gateway, net proxy, service discovery and certificate service. It also bills through Stripe, which is part of the design.
Who is it for?
Adopt Port Buddy if you need to expose a raw TCP or UDP service, such as a game server or a database, to the internet, because that is the capability the README says sets it apart from standard tunneling tools. Do not adopt it as a free local development utility, since the compose file carries Stripe price identifiers for Pro, Team and extra tunnels, which means a hosted account and billing are part of the normal path.
Can I use it commercially?
Yes. Apache-2.0 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 31 days ago.
What is it written in?
Mainly Java, 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

Three protocols from one command, and that is the differentiator

Port Buddy is described as a lightweight, high-performance ngrok alternative designed to instantly expose local development servers to the public internet, with native, zero-configuration support for HTTP and HTTPS, raw TCP and UDP protocols.

That last part is the reason to look at it. Most tunneling tools in this space are HTTP oriented, and when you want to expose a database or a game server you end up with ssh or a hand rolled proxy. The README is explicit that the difference is native TCP and UDP support rather than a wrapper.

The use cases listed follow from that. Test webhooks. Expose a local database. Debug an SSH connection. Host a local multiplayer game server. The UDP entry is called out as suitable for debugging IoT protocols and VoIP services and for hosting game servers, with Minecraft and Rust named.

The mechanism is a secure reverse tunnel with a single command, no router port forwarding required. The primary language is Java, the licence is Apache-2.0, the homepage is portbuddy.dev, and the last push to the repository was on 2026-09-22, with releases 1.0.16, 1.0.17 and 1.0.18 landing in July 2026.

So the architecture is not a single binary talking to a hosted relay, it is a full service with its own control plane, and that has consequences for how you use it.

The CLI surface, and the modes it distinguishes

The usage block is short, and the shape of it explains how the tool handles different protocol requirements.

text
Usage: portbuddy [options] [mode] [host:][port]

Modes:
  http (default), tcp, udp

Options:
  -d,  --domain=<domain>        Requested static subdomain (e.g. my-app)
  -pr, --port-reservation=<hp>  Use specific port reservation host:port for TCP/UDP
  -pc, --passcode=<passcode>    Protect tunnel with a passcode
  -v,  --verbose                Enable verbose logging
  -h,  --help                   Show help message
  -V,  --version                Show version info

The mode is a positional argument and http is the default, so the common case is just a port number. The options cover the three things you actually need to control: a stable name for the endpoint, a specific port on the public side, and a passcode in front of the tunnel.

The static subdomain option is worth noting against the generated URL alternative. The key features list says custom and static domains mean you can keep your endpoints permanent even across restarts, which is what the -d flag requests a subdomain for, for example my-app.

The passcode option is the access control story. It is a passcode on the tunnel, not per-user authentication, so it is the minimum a shared link needs rather than a permissions model.

The --port-reservation option takes a host and port for TCP and UDP, so the public side of a raw tunnel can be pinned to a known address, which is what a client application that expects a fixed endpoint needs.

Three one-liners, and what each one hands back

The quick start gives one command per protocol, and each one shows the shape of the public endpoint you get back.

A local web app on port 3000 is exposed with the port alone.

bash
portbuddy 3000

The output is a public URL like https://my-web-site.portbuddy.dev.

A local TCP service, such as a PostgreSQL database on port 5432, needs the mode.

bash
portbuddy tcp 5432

That one outputs a public endpoint like net-proxy-1.portbuddy.dev:43452, so the public side is a host and a port rather than a URL, which is what you would feed into a database connection string.

And a local UDP server on port 19132 takes the udp mode.

bash
portbuddy udp 19132

The pattern across the three is worth internalising, because it is the difference between this and a URL-based tunnel. The HTTP case gives you a hostname on a shared domain with an auto-generated SSL certificate. The TCP and UDP cases give you a host and port on a net-proxy host, and the port is assigned rather than chosen unless you ask for a reservation.

That distinction shows up in the architecture. There is a separate net-proxy module described as a high-performance TCP/UDP proxy, and the public endpoint host in the example is a net-proxy host, not the API gateway. So a raw tunnel does not go through the same path as an HTTP one, and the CLI's mode argument is telling the server which path to set up.

Eight modules, and what each one owns

The architecture section lists the modules, and the list is a map of the system.

The cli is a GraalVM-native command-line application on Java 25. The server is a Spring Boot 3.5.7 API and tunnel management component. The net-proxy is the high-performance TCP and UDP proxy. The gateway is a Webflux-based API gateway. The web module is a React dashboard written in TypeScript with TailwindCSS. The eureka module is service discovery. The ssl-service handles automated SSL certificate management. And common holds shared DTOs and utilities.

Eureka in the middle of that list is the notable choice. A service registry is how the CLI, the gateway and the net proxy find each other, which is what lets the system run as several containers on one host and lets a client be routed to whichever proxy instance is closest or least loaded.

The ssl-service is the other one with visible user impact. The key features list says HTTP and HTTPS tunnels come with auto-generated SSL certificates, and the module that produces them is separate, which means certificate issuance is not something the gateway does inline.

The repository top level matches the module list one for one, with cli/, common/, eureka/, gateway/, net-proxy/, server/, ssl-service/ and web/ directories, plus a pom.xml, a mvnw wrapper and a Dockerfile for each service, entrypoint scripts, a lombok.config and a media/ directory. The .env.sample at the root holds the configuration.

Running it yourself, and what the compose file expects

The development section lists the prerequisites: Java 25, Docker and Docker Compose, Spring Boot 3, Maven 3.9 or newer, and Node.js with npm for the web module.

The build is one command.

bash
./mvnw clean install

And the whole stack comes up with docker compose.

bash
docker-compose up -d

The compose file is where the real configuration surface shows. The Postgres service runs on postgres:16-alpine with a container name of pb-db, a restart policy of unless-stopped, credentials from environment variables, a port mapping of 5432 to 5432 and a volume at ${PWD}/pg_data mapped into the container data directory. The eureka and server services use network_mode host, and the eureka service pulls an image from a Yandex container registry rather than Docker Hub.

That registry detail is worth knowing if you are behind a firewall or mirror your images, because the other images you would expect to find on a public registry are not there.

The environment variables the stack expects come from .env.sample, and the list is long enough to be the real configuration contract. It covers the database connection, SMTP credentials, a Stripe API key and three Stripe price identifiers for Pro, Team and extra tunnels, a Stripe webhook secret, Google and GitHub OAuth client ids and secrets, a pair of JWT key file paths, the net proxy public host, its region and coordinates, the application domain, and the Eureka zone URL with Eureka username and password.

The Stripe and OAuth variables are the ones to notice. They say plainly that there is an account and billing layer in this system, with tiers, and they also say that Google and GitHub sign-in are part of it.

Where Port Buddy is the wrong tool

Three cases, and the first is about what you are paying for and who operates what.

Port Buddy is not a local binary you run and forget. The compose file has a Postgres instance, a service registry, an API server, a gateway, a net proxy, an SSL service and a web dashboard, and the configuration includes Stripe prices for Pro, Team and extra tunnels plus Google and GitHub OAuth credentials. If your requirement is a quick tunnel while you test a webhook, that is a lot of machinery, and a plain reverse proxy solves the same problem without an account with a payment provider attached.

The second is exposing something you should not expose. The tool exists to publish a local port to the internet. A passcode in front of a tunnel is a shared secret in a URL, not a per-user permission, and the whole model assumes the thing behind the port is safe to reach. Publishing a local database on 5432 to a public host and port is a decision about your network's blast radius, and the CLI makes it a single command.

The third is anything that needs a stable, contractual public address. Static subdomains and port reservations give you continuity across restarts, but they are features of the hosted service, and self-hosting the stack means you own the domain, the certificates and the proxy fleet yourself.

One more practical constraint the README leaves implicit. The CLI in Docker expects the token at /root/.port-buddy/token and mounts it from the host, so a containerised setup has to manage that file, and the documentation does not say how the token is obtained.

Editorial conclusion

Adopt Port Buddy if you need to expose a raw TCP or UDP service, such as a game server or a database, to the internet, because that is the capability the README says sets it apart from standard tunneling tools. Do not adopt it as a free local development utility, since the compose file carries Stripe price identifiers for Pro, Team and extra tunnels, which means a hosted account and billing are part of the normal path. Verify first which parts you run yourself, because the repository ships a docker-compose.yml with the server, gateway, net proxy, Eureka and a Postgres 16 instance, and self-hosting that is a different commitment from running the CLI.

Frequently asked questions

What is Port Buddy?

It is a lightweight, high-performance ngrok alternative that exposes local development servers to the public internet, with native zero-configuration support for HTTP and HTTPS, raw TCP and UDP. The homepage is portbuddy.dev and the licence is Apache-2.0.

How do I expose a local port with Port Buddy?

Run portbuddy with the port for HTTP, which is the default mode, portbuddy tcp with the port for a raw TCP service such as PostgreSQL or SSH, and portbuddy udp with the port for a UDP service such as a game server.

What options does the Port Buddy CLI accept?

The options are -d for a requested static subdomain, -pr for a specific port reservation host and port for TCP and UDP, -pc to protect the tunnel with a passcode, -v for verbose logging, plus -h for help and -V for version info. The modes are http as default, tcp and udp.

What are the modules in the Port Buddy architecture?

A GraalVM-native CLI on Java 25, a Spring Boot 3.5.7 server for API and tunnel management, a high-performance TCP and UDP net proxy, a Webflux-based gateway, a React and TailwindCSS web dashboard, Eureka for service discovery, an ssl-service for automated certificate management, and a common module for shared DTOs and utilities.

How do I run Port Buddy with Docker?

The CLI can run in a container by pulling the image with docker pull amaktech/portbuddy:latest, and the token must be mounted from the host to /root/.port-buddy/token. To run the whole stack, the repository ships a docker-compose.yml and the documented command is docker-compose up -d.

Official sources

  1. amak-tech/port-buddy on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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/amak-tech-port-buddy.svg)](https://hysenlabs.com/projects/amak-tech-port-buddy)

Was this useful?