Documentation
Get up and running with SubTunnel in under a minute.
Quick Start
Install the CLI, point it at your server, and your local port is live on the internet.
# 1. Install SubTunnel
$ curl -sSL https://www.subtunnel.dev/install.sh | sh
# 2. Expose a local port through your server
$ subtunnel local 3000 \
--to your-server.example.com:7835 \
--token YOUR_TOKEN \
--subdomain myappYour local server on port 3000 is now accessible at https://myapp.your-domain.com
Installation
Install the SubTunnel CLI on macOS or Linux. A single binary, no dependencies.
macOS / Linux
$ curl -sSL https://www.subtunnel.dev/install.sh | shSupports macOS (Apple Silicon & Intel) and Linux (x86_64 & ARM64). The script detects your platform, downloads the latest release, and installs to /usr/local/bin.
Manual Download
Download the binary for your platform from the GitHub Releases page, extract it, and place it in your PATH.
Self-Hosting the Server
SubTunnel is fully self-hosted. You run the server on your own VPS (EC2, DigitalOcean, Hetzner, etc.) and connect clients to it. Here's how to set it up from scratch.
Prerequisites
- A VPS with a public IP address (any Linux distro)
- A domain name with DNS access (e.g. Cloudflare, Route 53)
- Ports 7835 (control plane) and 8080 (HTTP traffic) open in your security group / firewall
1. DNS Setup
Point your domain and a wildcard subdomain to your server's IP address. This allows SubTunnel to route traffic to tunnels based on subdomain.
# Replace 203.0.113.10 with your server's public IP
A tunnel.example.com → 203.0.113.10
A *.tunnel.example.com → 203.0.113.102. Install SubTunnel on Your Server
$ curl -sSL https://www.subtunnel.dev/install.sh | sh3. Generate a Token
Create a shared secret that clients will use to authenticate with the server.
$ openssl rand -hex 16
# e.g. 1f881630ba330f5b4631070118b5d9094. Set Up nginx (TLS Termination)
Use nginx as a reverse proxy to handle TLS termination and forward HTTP traffic to SubTunnel's HTTP listener. This gives you automatic HTTPS via Let's Encrypt.
# Wildcard HTTPS — routes *.tunnel.example.com to SubTunnel
server {
listen 443 ssl;
server_name *.tunnel.example.com;
ssl_certificate /etc/letsencrypt/live/tunnel.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tunnel.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}For wildcard certificates, use DNS-based validation with certbot: certbot certonly --dns-cloudflare -d tunnel.example.com -d *.tunnel.example.com
5. Start the Server
$ subtunnel server \
--domain tunnel.example.com \
--token YOUR_TOKEN \
--port 7835 \
--http-port 80806. Run as a systemd Service
For production, run SubTunnel as a systemd service so it starts on boot and auto-restarts.
[Unit]
Description=SubTunnel Server
After=network.target
[Service]
Type=simple
User=subtunnel
ExecStart=/usr/local/bin/subtunnel server \
--domain tunnel.example.com \
--token YOUR_TOKEN \
--port 7835 \
--http-port 8080
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target$ sudo systemctl enable subtunnel
$ sudo systemctl start subtunnel
$ sudo systemctl status subtunnel7. Connect a Client
From your local machine, connect to your server:
$ subtunnel local 3000 \
--to your-server.example.com:7835 \
--token YOUR_TOKEN \
--subdomain myapp
# ✓ Connected
# Forwarding: https://myapp.tunnel.example.com → localhost:3000Architecture Overview
Internet → nginx (TLS :443) → SubTunnel HTTP (:8080) → route by Host header
↕ yamux streams
Client (subtunnel local) ←— TLS + yamux (:7835) ——→ SubTunnel Server
↕ (control + data)
localhost:PORTCLI Reference
subtunnel server
Run the SubTunnel server on your VPS. This is the public-facing component that accepts client connections and routes traffic.
$ subtunnel server --domain tunnel.example.com --token SECRET
# All options:
$ subtunnel server \
--domain tunnel.example.com \ # Required: base domain for subdomains
--token SECRET \ # Auth token clients must provide
--port 7835 \ # Control plane port (default: 7835)
--http-port 8080 \ # HTTP listener port (default: 8080)
--host 0.0.0.0 \ # Bind address (default: 0.0.0.0)
--extra-domain other.com # Accept additional domainssubtunnel local
Connect to a SubTunnel server and expose a local port to the internet.
$ subtunnel local 3000 --to server:7835 --token SECRET
# With a custom subdomain:
$ subtunnel local 3000 \
--to server.example.com:7835 \
--token SECRET \
--subdomain myapp # → myapp.tunnel.example.com
# Skip TLS verification (self-signed certs):
$ subtunnel local 3000 --to server:7835 --token SECRET --tls-verify false
# Use a custom CA certificate:
$ subtunnel local 3000 --to server:7835 --token SECRET --tls-ca /path/to/ca.pemConfiguration
SubTunnel can be configured via a subtunnel.yml file in your project root:
server:
url: https://tunnel.example.com
tunnels:
web:
proto: http
addr: 3000
domain: app.example.com
api:
proto: http
addr: 8080
domain: api.example.comThen start all tunnels with: subtunnel start