SubTunnel

Quick Start

Up and running in a minute

Install the CLI, connect it to your SubTunnel server, and expose a local port on your own domain.

subtunnel · zsh

$ curl -sSL https://www.subtunnel.dev/install.sh | sh

$ subtunnel local 3000 --to your-server.example.com:7835 \

--token YOUR_TOKEN --subdomain myapp

Forwarding https://myapp.your-server.example.com -> localhost:3000

Installation

Install the binary

The install script detects your platform and places one static binary in /usr/local/bin. It supports macOS and Linux.

$ curl -sSL https://www.subtunnel.dev/install.sh | sh

Manual download. Download the binary for your platform from GitHub Releases, extract it, and place it in your PATH.

Self-Hosting

Run your own server

Everything below runs on one small VPS. You need a domain and a server with ports 80, 443, and 7835 reachable.

  1. 01

    Point DNS at your server

    Create A records for the tunnel domain and its wildcard, both pointing to your server's public IP address.
    DNS records
    A     tunnel.example.com      → 203.0.113.10
    A     *.tunnel.example.com    → 203.0.113.10
  2. 02

    Install SubTunnel on the server

    SSH into the VPS and install the same static binary used by the client.
    $ curl -sSL https://www.subtunnel.dev/install.sh | sh
  3. 03

    Generate a token

    Create a shared secret that clients will use to authenticate with the server.
    $ openssl rand -hex 16
  4. 04

    Set up nginx TLS termination

    Use nginx to terminate TLS and forward HTTP traffic to SubTunnel's HTTP listener on port 8080.
    nginx.conf
    # 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. 05

    Start the server

    Start the control plane on port 7835 and the internal HTTP listener on port 8080.
    $ subtunnel server --domain tunnel.example.com --token YOUR_TOKEN --port 7835 --http-port 8080
  6. 06

    Run as a systemd service

    For production, run SubTunnel as a systemd service so it starts on boot and restarts automatically.
    /etc/systemd/system/subtunnel.service
    [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
    Enable and start
    $ sudo systemctl enable subtunnel
    $ sudo systemctl start subtunnel
    $ sudo systemctl status subtunnel
  7. 07

    Connect a client

    From your local machine, connect a port to the server and request its public subdomain.
    $ subtunnel local 3000 --to tunnel.example.com:7835 --token YOUR_TOKEN --subdomain myapp

CLI Reference

Four focused command areas.

Run the public server, connect one local port directly, start configured tunnels, or manage the client as a native service.

subtunnel server

Run the public-facing server that accepts client connections and routes traffic.

FlagDescription
--portControl-plane listen port. Default: 7835.
--http-portHTTP listener receiving proxied traffic from nginx. Default: 8080.
--hostBind address. Default: 0.0.0.0.
--domainRequired. Domain for tunnel subdomains.
--extra-domainAdditional accepted domain. Repeatable.
--tokenAuthentication token agents must provide. Env: SUBTUNNEL_TOKEN.
--tls-certTLS certificate PEM path.
--tls-keyTLS private key PEM path.

subtunnel local

Connect to a SubTunnel server and expose one local port.

FlagDescription
<port>Positional local port to expose.
--toServer address in host:port format.
--tokenAuthentication token. Env: SUBTUNNEL_TOKEN.
--subdomainRequest a specific subdomain.
--tls-verifyVerify the server TLS certificate. Default: true. Set false for self-signed certificates.
--tls-caCustom CA certificate PEM path.

subtunnel run

Read a TOML config and start all tunnels, or a named subset, in one process. Each tunnel has its own connection and reconnect loop. Keeping the token in the config avoids exposing it in the process list.

config.toml
server = "tunnel.example.com:7835"
token = "YOUR_TOKEN"

[tunnels.myapp]
local_port = 3000
subdomain = "myapp"
$ subtunnel run --all
$ subtunnel run myapp --config /absolute/path/to/config.toml

subtunnel service

Install the configured client agent as a systemd service on Linux or a launchd service on macOS. Normal users install a user service. Running the command with sudo installs a system service. SubTunnel does not manage your application process.

$ subtunnel service install --config /absolute/path/to/config.toml
$ subtunnel service status
$ subtunnel service generate systemd --config /absolute/path/to/config.toml