Personal ngrok alternative. Expose local ports to the internet with automatic HTTPS, an interactive TUI dashboard, and HTTP request inspection -- all through your own VPS.
- A VPS (any provider) with ports 80 and 443 open (nothing else running on them for now)
- Docker + Docker Compose on the VPS
- A domain name you control
- An SSH key on your Mac/Linux (You can run
ssh-keygento generate one if you don't have one)
Add a wildcard A record with your DNS provider (Cloudflare, Vercel, Namecheap, etc):
| Type | Name | Value |
|---|---|---|
| A | * | <your-vps-ip> |
curl -fsSL https://raw.githubusercontent.com/R44VC0RP/pgrok/main/install.sh | bash -s client(if you have a windows machine, and you get this running, please open a PR!)
You'll be prompted for your VPS IP, domain, and email. The installer will:
- Auto-detects your SSH key
- Builds and installs the
pgrokcommand - Copies a server setup command to your clipboard (with your SSH key embedded)
SSH into your VPS and paste the command from step 2. Or run:
curl -fsSL https://raw.githubusercontent.com/R44VC0RP/pgrok/main/install.sh | sudo bash -s serverThe script installs Caddy, configures SSH tunneling, adds your SSH key, and starts everything.
pgrok myapp 4000
# => https://myapp.yourdomain.com -> localhost:4000# Expose a local dev server
pgrok myapp 4000
# -> https://myapp.yourdomain.com
# Expose an API
pgrok api 3000
# -> https://api.yourdomain.com
# Any subdomain works instantly
pgrok staging 8080
# -> https://staging.yourdomain.com
# Debug mode -- dumps raw tunnel logs on exit
pgrok myapp 4000 --print-logsPress Ctrl+C to stop. The route is cleaned up automatically.
Rebuild the binary after pulling updates:
./setup.sh client --rebuild
- Caddy on the VPS handles HTTPS with on-demand TLS -- certs are auto-provisioned per subdomain. Falls back to ZeroSSL if Let's Encrypt is rate-limited.
- SSH reverse tunnels carry traffic -- no extra tunnel software.
- A small Python script on the server dynamically configures Caddy routes when tunnels connect/disconnect.
- The TUI client (built with OpenTUI) provides a live dashboard with request inspection, connection stats, and color-coded HTTP logs.
The dashboard shows in real-time:
- Session Status -- connecting / provisioning TLS / online / error
- Forwarding -- your public URL and local port
- TLS Certificate -- provisioning status (Let's Encrypt)
- Connection Stats -- total requests, open connections, request rates (1m/5m), response time percentiles (p50/p90)
- HTTP Request Log -- scrollable, color-coded log of every request through the tunnel
Request log colors:
- Methods: GET (blue), POST (purple), PUT/PATCH (yellow), DELETE (red)
- Status: 2xx (green), 3xx (cyan), 4xx (yellow), 5xx (red)
- Duration: <100ms (green), 100-500ms (yellow), >500ms (red)
- A request arrives for
myapp.yourdomain.com - Caddy checks with
pgrok-ask: "Should I get a cert for this domain?" pgrok-askverifies it's a single-level subdomain of*.yourdomain.com- Caddy uses HTTP-01 challenge to get a cert -- tries Let's Encrypt first, falls back to ZeroSSL if rate-limited
- Cert is cached and auto-renewed
- The TUI client triggers cert provisioning during tunnel setup, so it's ready before you open the URL
- SSH key authentication only (no passwords)
- Dedicated
pgrokuser with restricted SSH (remote forwarding only) - Caddy admin API only on localhost (not exposed externally)
- SSH tunnels bind to localhost only (
GatewayPorts no) pgrok-askprevents cert abuse -- only allows single-level subdomains
Generated by the installer. Edit manually if needed:
PGROK_HOST=your-vps-ip
PGROK_DOMAIN=yourdomain.com
PGROK_USER=pgrok
PGROK_SSH_KEY=~/.ssh/id_ed25519| File | Purpose |
|---|---|
Caddyfile |
On-demand TLS config with LE + ZeroSSL |
docker-compose.yml |
Caddy container |
Script (/usr/local/bin/) |
Purpose |
|---|---|
pgrok-tunnel |
Manages Caddy routes + provisions TLS certs |
pgrok-ask |
Validates cert requests (systemd service) |
Stuck on "connecting" in the TUI:
- Run with
--print-logs, press Ctrl+C, check/tmp/pgrok-debug.log - Verify SSH works:
ssh pgrok@your-vps-ip echo ok
Stuck on "provisioning TLS...":
- Let's Encrypt may be rate-limited (50 certs/week). ZeroSSL fallback handles this.
- Check Caddy logs:
docker compose logs caddyin/opt/pgrok
SSL certificate errors:
- Verify
pgrok-askis running:systemctl status pgrok-ask - Make sure ports 80 and 443 are open
- Cloudflare proxy (orange cloud) must be OFF for the wildcard record
cd client/tui
bun install
bun run index.ts myapp 4000 # dev mode
bun run build # compile binary
bun run tsc --noEmit # type-check- Single user (personal tool, not multi-tenant)
- Mac and Linux only (no Windows)
- No automatic reconnection (restart
pgrokif connection drops) - Stale routes possible on abrupt disconnection (self-heal on next connect)
- HTTP request logging only (WebSocket passthrough works but isn't logged)
We welcome contributions! Please open an issue or PR.
MIT