Everything tunn3l does, from one-liners to always-on tunnels with permanent URLs.
A single curl downloads a standalone binary. No Node, no Python, no Docker, no package manager. Works on macOS and Linux, ARM and x64.
$ curl -sSf https://tunn3l.sh/install | sh tunn3l installed! Open a new terminal, then: tunn3l http 3000 # or via npm: $ npx tunn3l http 3000
Expose any local port to the internet. Your dev server, your API, your webhook receiver — instantly reachable at a *.tunn3l.sh URL.
$ tunn3l http 3000 tunn3l tunnel ready forwarding https://glass-moss.tunn3l.sh → http://localhost:3000
Expose SSH, databases, or any TCP service. Each tunnel gets its own port on the relay. Connect from anywhere without VPNs or firewall changes.
$ tunn3l ssh tunn3l tcp tunnel ready forwarding https://my-box.tunn3l.sh → tcp://localhost:22 connect: ssh user@tunn3l.sh -p 34821
Install tunn3l as a system service. It starts on boot, reconnects automatically, and stays up forever. One command to install, one to start.
$ tunn3l daemon install --port 3000 --subdomain my-server tunn3l daemon "default" installed $ tunn3l daemon start tunn3l daemon "default" started forwarding https://my-server.tunn3l.sh
Uses launchd on macOS, systemd on Linux. Check on it anytime with tunn3l daemon status.
Reserve a subdomain and it's yours. Every time your tunnel connects, it gets the same name — no more random URLs that change on every restart.
Reserve subdomains from the dashboard or let your daemon config lock one in with --subdomain.
Install tunn3l on every machine with the same API key. Each device gets its own reserved subdomain, all managed from one dashboard.
# On your second machine: $ curl -sSf https://tunn3l.sh/install | sh -s -- --key tk_your_key $ tunn3l daemon install --port 3000 --subdomain my-laptop $ tunn3l daemon start forwarding https://my-laptop.tunn3l.sh
See all your active tunnels, reserve and release subdomains, claim your account — all from tunn3l.sh/dashboard. No signup required to start. Claim with an email whenever you're ready.
Every command supports --json for machine-readable output. Clean exit codes (0 = clean, 1 = auth, 2 = connection, 3 = subdomain taken). Fully configurable via environment variables. Zero interactive prompts — agents can install, connect, and manage tunnels without human intervention.
$ tunn3l http 3000 --json {"url":"https://glass-moss.tunn3l.sh","subdomain":"glass-moss"}
Everything you can do in the dashboard, you can do from the terminal. No browser needed.
$ tunn3l status API key: tk_d99c... claimed: you@example.com Reserved subdomains: my-server.tunn3l.sh my-laptop.tunn3l.sh Active tunnels: https://my-server.tunn3l.sh (http) $ tunn3l reserve my-new-box Reserved: my-new-box.tunn3l.sh $ tunn3l unreserve my-new-box Released: my-new-box.tunn3l.sh $ tunn3l claim you@example.com Account claimed: you@example.com $ tunn3l status --json {"api_key":"tk_d99c...","claimed":true,...}
The --json flag on status returns structured data — perfect for agents that need to read account state programmatically.
Your API key lives in ~/.tunn3l/config.json by default. Move it to an environment variable or a dotenv file if you prefer:
// ~/.tunn3l/config.json { "api_key_source": "env:TUNN3L_API_KEY" } // or read from a file: { "api_key_source": "file:~/.env.local:TUNN3L_API_KEY" }
MIT licensed. The relay, the CLI, the installer — everything is on GitHub. Run your own relay if you want. Point the CLI at it with --relay wss://your-server/ws/connect.