PortHub is a self-hosted control plane for Rathole. It gives you a web UI and API to register machines, create forwarding rules, generate Rathole config automatically, and bootstrap remote clients with a single install command.
Instead of hand-editing server.toml and client.toml files across multiple systems, PortHub keeps the tunnel state in one place and handles the repetitive operational work for you.
- Machine inventory, tokens, grouping, and status tracking
- Forwarding rules that map one public port to one machine and one local service
- Automatic generation of Rathole
server.tomland per-machineclient.toml - Remote machine bootstrap for Linux (
systemd) and macOS (launchd) - Live status, config change checks, and client heartbeat flow
- Per-port IP filtering for restricting exposed services to approved source IPs
- User authentication and interactive API docs at
/api/docs
PortHub can apply an IP filter to each forwarding rule.
- By default, a forwarded port is public.
- If you add allowed source IPs, PortHub treats that port as restricted and only those IPv4 addresses are allowed through.
- The UI exposes this in the forwarding rule editor as
IP Filter. - The firewall service applies the policy at the external port level and keeps recent hit / blocked-IP data for traffic monitoring.
This is backed by the firewall/ service, which stores per-port policies and programs nftables rules on the host. If firewall integration is not configured, the rest of PortHub still works, but IP filtering and firewall traffic visibility will not be available.
PortHub is made of a few small pieces:
api/: FastAPI backend for auth, machines, connections, bootstrap endpoints, and Rathole config generationui/: Next.js dashboard for users, machines, groups, and forwarding rulesproxy/: nginx entry point for the UI, API, and WebSocket trafficrathole: managed server container that runs the generatedserver.tomlfirewall/: optional per-port firewall service for IP filtering and traffic samplingmongodb: persistent app dataredis: session storage and lightweight runtime coordination
.
├── api/ FastAPI app and client bootstrap assets
├── firewall/ Firewall service for port policy and traffic sampling
├── ui/ Next.js frontend
├── proxy/ nginx config and certificate helper
├── docker-compose.yml Development app stack
├── docker-compose-prod.yml Production app stack
├── docker-compose-services.yml MongoDB and Redis
├── env.example Example environment configuration
└── deploy.sh Production app redeploy helper
- Docker
- Docker Compose
- OpenSSL
- A Linux host if you want to use the firewall/IP filtering service, since it applies nftables rules with host networking
cp env.example .envReview these settings first:
HOSTAPP_HTTP_PORTAPP_HTTPS_PORTPORT_HUB_PUBLIC_BASE_URLRATHOLE_SERVER_ADDRESSRATHOLE_PORTEXTERNAL_PORT_RANGE_STARTEXTERNAL_PORT_RANGE_END
If you want IP filtering enabled, also review:
FIREWALL_BASE_URLFIREWALL_API_KEYFW_API_KEYFW_DB_PATHFW_RECENT_IP_TTLFW_RECENT_IP_HISTORY_LIMITFW_NFT_TABLE
Notes:
PORT_HUB_PUBLIC_BASE_URLshould be the public URL users and clients reach.RATHOLE_SERVER_ADDRESSshould be the address remote Rathole clients connect to.EXTERNAL_PORT_RANGE_STARTandEXTERNAL_PORT_RANGE_ENDlimit which public ports PortHub can assign.FIREWALL_API_KEYandFW_API_KEYmust match.
cd proxy
bash generate_certificate.sh
cd ..docker compose -f docker-compose-services.yml up -dFor development:
docker compose -f docker-compose-services.yml -f docker-compose.yml up -d --buildFor production-style runtime:
docker compose -f docker-compose-services.yml -f docker-compose-prod.yml up -d --builddeploy.sh rebuilds and restarts only the app stack from docker-compose-prod.yml, so MongoDB and Redis still need to be running separately.
Visit:
http://<host>:<APP_HTTP_PORT>https://<host>:<APP_HTTPS_PORT>
API docs are available at:
/api/docs
- Create an account and sign in.
- Add a machine and copy its generated install command.
- Create one or more forwarding rules for that machine.
- Optionally add an
IP Filterallowlist for any port that should not be public. - Run the install command on the remote machine and let the client fetch config automatically.
After bootstrap, the machine client authenticates with PortHub, downloads its managed client.toml, starts Rathole locally, and keeps checking for config changes.
- nginx proxies
/to the UI and/apiplus/socket.ioto the API - The Rathole server reads
/runtime/rathole/server.toml - The
ratholeandfirewallservices use host networking in the provided Compose files - The API can run without firewall integration, but connection-level IP filtering depends on the
firewallservice being reachable
API_SECRET_KEY: required for API startupSIGNUP_DISABLED: disable public account creationPORT_HUB_PUBLIC_BASE_URL: canonical public URL for generated machine endpointsRATHOLE_SERVER_ADDRESS: explicit server address for remote clientsRATHOLE_RELEASE_GITHUB_REPOSITORY: source repo for Rathole release downloadsRATHOLE_RELEASE_CACHE_TTL_SECONDS: cache lifetime for downloaded Rathole binariesMACHINE_CONFIG_LONG_POLL_TIMEOUT_SECONDS: long-poll wait window for config change checksEXTERNAL_PORT_RANGE_START/EXTERNAL_PORT_RANGE_END: allowed external port rangeFIREWALL_BASE_URL: API URL the main backend uses to reach the firewall serviceFW_NFT_TABLE: nftables table name managed by the firewall service
PortHub does not replace Rathole. It makes Rathole easier to operate once you have more than one machine, more than one tunnel, or stricter operational requirements around bootstrap, visibility, and access control.