Self-hosting
tnnl is built to be self-hosted. Run the tunnel server and PostgreSQL — and, for the full experience, Caddy as the TLS front door and the Next.js dashboard — with Docker Compose.
The components
- tunnel server — accepts public traffic and the CLI control connection, and serves the API.
- PostgreSQL — stores users, sessions, and tunnel metadata; enables login auth and the API.
- Caddy — the TLS front door that terminates HTTPS for the wildcard and custom domains.
- Next.js dashboard — the web UI for managing accounts and tunnels.
$ docker compose up -dPointing the CLI at your server
The hosted tnnl.uz is the default the CLI talks to. To use your own server, point the CLI at it with the TUNNEL_SERVER (control host:port), TUNNEL_API_URL, and TUNNEL_WEB_URL environment variables — or the equivalent --server, --api, and --web flags.
Server configuration flags
These are the tunnel-server binary flags:
| Flag | Description |
|---|---|
--domain | Base domain; subdomains are allocated under it. |
--http-port | Public HTTP port (default 8080). |
--control-port | CLI control connection port (default 7000). |
--bind | Bind address (default 0.0.0.0). |
--public-ip | Server public IP; enables custom-domain DNS verification. |
--no-tls | Serve plaintext, e.g. when Caddy terminates TLS. |
--acme / --acme-email / --acme-domain | Built-in ACME for a fixed hostname set. |
--database-url | Postgres connection; enables login auth and the API. |
--api-port | API port (default 4000). |
--default-admin-user / --default-admin-password | Seed the first superadmin. |
--token | Legacy shared secret when running without a database. |
--inspect-ring-cap | Retained requests per tunnel (default 50). |
--inspect-body-cap | Captured body bytes (default 32768). |
--cli-install-base | Base URL the CLI installs/upgrades from (env TNNL_CLI_INSTALL_BASE). |
DNS
Point a wildcard *.yourdomain (and the apex) at the host so subdomains route. TLS for the wildcard is typically handled by Caddy via a DNS-01 challenge, while custom domains use on-demand TLS.
Database migrations
Database migrations run automatically on server boot.
Building from source
Building from source requires the Rust toolchain. Production images are built in CI and pulled by the host — the host never compiles.