A self-hosted ngrok alternative built in Rust. Expose your local services to the internet through secure tunnels.
# Clone and build
git clone https://github.com/copyleftdev/zgrok.git
cd zgrok
cargo build --release
# Binaries are in target/release/
ls target/release/zgrok target/release/zgrok-edge# Start a tunnel to your local server on port 8080
./target/release/zgrok http 8080
# With a custom subdomain
./target/release/zgrok http 8080 --subdomain myapp
# With authentication
./target/release/zgrok http 8080 --authtoken zg_your_token_here# Generate TLS certificates (or use Let's Encrypt)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
# Start the edge server
./target/release/zgrok-edge \
--cert cert.pem \
--key key.pem \
--domain yourdomain.com \
--https-port 443 \
--http-port 80┌─────────────┐ HTTPS ┌─────────────┐ Multiplexed ┌─────────────┐
│ Browser │ ──────────────▶│ Edge │◀───────────────── │ Agent │
│ │ subdomain. │ Server │ Tunnel │ (CLI) │
└─────────────┘ domain.com └─────────────┘ └──────┬──────┘
│ │
│ │ HTTP
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Control │ │ Your │
│ Plane │ │ Service │
└─────────────┘ └─────────────┘
| Component | Crate | Description |
|---|---|---|
| Agent | zgrok-agent |
CLI client that runs on your machine |
| Edge | zgrok-edge |
Public server that terminates TLS and routes traffic |
| Control | zgrok-control |
Authentication, sessions, subdomain allocation |
| Protocol | zgrok-protocol |
Binary framing, stream multiplexing, keepalive |
| Common | zgrok-common |
Shared utilities, observability, validation |
zgrok - Expose local services via secure tunnels
USAGE:
zgrok [OPTIONS] <COMMAND>
COMMANDS:
http Start an HTTP tunnel
tcp Start a TCP tunnel
config Manage configuration
status Show tunnel status
OPTIONS:
-c, --config <PATH> Path to config file
-v, --verbose Increase verbosity (-v, -vv, -vvv)
--no-color Disable colored output
zgrok http [OPTIONS] <PORT>
ARGUMENTS:
<PORT> Local port to expose
OPTIONS:
--host <HOST> Local host to forward to [default: localhost]
-s, --subdomain <SUBDOMAIN> Request specific subdomain
--authtoken <TOKEN> API key for authentication
--region <REGION> Edge region to connect to
--inspect Enable request inspection UI
Create ~/.config/zgrok/config.toml:
authtoken = "zg_your_token_here"
region = "us-east"
[defaults]
inspect = true
[tunnels.webapp]
proto = "http"
addr = "8080"
subdomain = "myapp"
[tunnels.api]
proto = "http"
addr = "3000"
subdomain = "api"zgrok-edge - Edge/gateway server for zgrok tunneling service
OPTIONS:
--https-port <PORT> HTTPS listen port [default: 443]
--http-port <PORT> HTTP redirect port, 0 to disable [default: 80]
--listen <ADDR> Listen address [default: 0.0.0.0]
--cert <PATH> TLS certificate (PEM)
--key <PATH> TLS private key (PEM)
--domain <DOMAIN> Base domain for subdomains [default: zgrok.io]
--control-url <URL> Control plane URL
-v, --verbose Enable verbose logging
| Variable | Description |
|---|---|
ZGROK_AUTHTOKEN |
Default authentication token |
ZGROK_CERT_PATH |
TLS certificate path |
ZGROK_KEY_PATH |
TLS private key path |
ZGROK_CONTROL_URL |
Control plane endpoint |
FROM rust:1.75 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/zgrok-edge /usr/local/bin/
EXPOSE 443 80
ENTRYPOINT ["zgrok-edge"]# /etc/systemd/system/zgrok-edge.service
[Unit]
Description=zgrok Edge Server
After=network.target
[Service]
Type=simple
User=zgrok
ExecStart=/usr/local/bin/zgrok-edge \
--cert /etc/zgrok/cert.pem \
--key /etc/zgrok/key.pem \
--domain example.com
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetConfigure your DNS with a wildcard A record:
*.tunnel.example.com A <your-edge-server-ip>
Then run the edge server with --domain tunnel.example.com.
- Stream Multiplexing: Multiple logical streams over a single TCP connection
- TLS Termination: Secure connections with automatic HTTPS
- Subdomain Routing: Each tunnel gets a unique subdomain
- Rate Limiting: Protection against abuse (per-IP, per-account, per-subdomain)
- Connection Limits: Resource protection with configurable limits
- Health Checks: Kubernetes-ready liveness and readiness probes
- Prometheus Metrics: Built-in observability
- Distributed Tracing: OpenTelemetry support
- Graceful Shutdown: Clean connection draining
# Run all tests
cargo test
# Run with verbose output
cargo test -- --nocapture
# Lint
cargo clippy -- -D warnings
# Format
cargo fmt
# Build release
cargo build --releasecrates/
├── zgrok-protocol/ # Wire protocol, frame codec, multiplexer
├── zgrok-agent/ # CLI client with TUI
├── zgrok-edge/ # Public-facing gateway server
├── zgrok-control/ # Auth, sessions, subdomain allocation
├── zgrok-common/ # Shared types, observability, validation
└── zgrok-integration-tests/ # Cross-crate integration tests
MIT
See CONTRIBUTING.md for guidelines.