THE FIELD GUIDE
TAILMUX DOCUMENTATION

Port forwarding & local URLs

Reach remote loopback services through localhost or a custom name.

tailmux forward personal/devbox 3000             # localhost:3000 → remote localhost:3000
tailmux forward personal/devbox 3000-3010        # inclusive range (up to 100 ports)
tailmux forward personal/devbox 8080:3000        # local:remote mapping
tailmux forward personal/devbox 3000-3005 --name devbox.localhost
tailmux forward work/buildbox 3000-3005 --name buildbox.localhost
tailmux forwards                                # IDs, routes and status
tailmux forwards --json
tailmux unforward <id>

Without --name, forwarding carries arbitrary TCP unchanged over SSH to the remote machine's loopback interface. Using the same local and remote port preserves localhost URLs, including OAuth callbacks registered for localhost. Local listeners bind only to 127.0.0.1; they are not exposed on your LAN. SSH must allow local forwarding; no Tailmux installation or additional service is needed remotely.

With --name, Tailmux runs an HTTP reverse proxy. Different names can share the same local port across boxes. Open http://devbox.localhost:3000 in your browser. Modern browsers resolve .localhost names to loopback; other clients may need explicit resolution (for example, curl's --resolve devbox.localhost:3000:127.0.0.1). Custom names such as devbox.local or dev.example.test work when your resolver maps them to 127.0.0.1. Add 127.0.0.1 devbox.local to /etc/hosts or configure your local DNS; Tailmux does not change system DNS. .local otherwise belongs to multicast DNS and is not automatically registered by Tailmux.

Named forwards rewrite HTTP Location, Content-Location and Refresh redirects pointing directly to localhost, 127.0.0.1 or ::1 on ports in that forward group. JSON URL strings are also rewritten, covering common authentication responses, with a 2 MiB buffering limit. Loopback cookie domains are removed to make cookies host-only; Secure, HttpOnly and SameSite attributes are preserved. Requests use the upstream localhost Host header; matching Origin and Referer URLs are translated back. WebSocket upgrades and streaming responses use Go's reverse proxy. Use --no-rewrite to disable response, Origin and Referer rewriting.

This does not rewrite HTML/JavaScript, external URLs containing OAuth redirect_uri parameters, or HTTPS URLs. OAuth provider callback allowlists, HTTPS requirements and application origin validation may still require configuration. For a strict localhost-only login flow, use the unnamed forward with the original port. Named mode serves HTTP; use raw TCP for end-to-end TLS or non-HTTP protocols. Two raw forwards cannot own the same local port. Unknown HTTP hostnames are rejected.

Forwards run in the local daemon after the CLI exits. They stop with unforward, tailmux stop, or daemon shutdown; they are not automatically restored. SSH failures appear in forwards and release the ports; recreate the forward to reconnect. Port ranges are reserved together: a conflict rejects the new group without disturbing existing forwards. Each group uses one SSH connection and private temporary Unix sockets. Forward creation uses your existing SSH configuration and agent in batch mode, so unlock the agent first. Automatic process selection is not yet implemented.

Inspect and stop a forward

tailmux forward ... --json returns an array with the created group's id, spec, and state. tailmux forwards --json lists groups, including failed ones. Use the returned ID with tailmux unforward <id>; the whole group, including every port in its range, is stopped.

tailmux forward lab/worker 3000 --name worker.localhost --json
tailmux forwards --json

Options follow the host and may appear before or after port arguments. There is no automatic process picker or port discovery yet.

Public URLs with Cloudflare or ngrok

You can publish one forwarded HTTP port through a separately installed tunnel client. Tailmux currently has no --cloudflare or --ngrok flags; the commands below use the provider's CLI alongside Tailmux.

Start an unnamed forward on a spare local port:

tailmux forward personal/devbox 13000:3000

The route is: public HTTPS URL → local tunnel client → 127.0.0.1:13000 → Tailmux SSH → remote 127.0.0.1:3000. Only this selected port is published. Use another local port for another box.

Cloudflare: temporary preview

After installing cloudflared, run locally:

cloudflared tunnel --url http://127.0.0.1:13000

This prints a random trycloudflare.com HTTPS URL. It is a temporary preview, not a permanent address. See Quick Tunnels for limitations.

Cloudflare: stable hostname

For a reusable address such as preview.example.com, use a named tunnel and a domain managed in your Cloudflare account. Following the Cloudflare setup guide:

  1. Create a tunnel in Cloudflare and run its connector on the same computer as the Tailmux forward.
  2. Add a published application route for preview.example.com.
  3. Set its service URL to http://127.0.0.1:13000.
  4. Keep the connector running and reopen the same Tailmux forward whenever needed.

The hostname stays the same across connector restarts. Cloudflare's dashboard creates the DNS record for the published route; see hostname routing. Keep connector credentials outside the repository.

ngrok: account domain

Install ngrok and authenticate it using your account's setup instructions. Then run:

ngrok http http://127.0.0.1:13000

The free plan includes an automatically assigned development domain; it does not let you choose a custom domain. See ngrok's current account limits.

To explicitly select a domain already available in your ngrok account:

ngrok http http://127.0.0.1:13000 --url https://YOUR-ASSIGNED-DOMAIN.ngrok-free.app

Replace the example with your actual domain. Custom domains require the relevant account support and DNS setup. The ngrok CLI reference documents --url.

Redirects, access and uptime

Use an unnamed Tailmux forward for these examples. Named routes match the HTTP Host header and reject unknown public hostnames. Their localhost rewriting targets the local HTTP name and port, not the public HTTPS origin.

A tunnel alone does not fix an application that emits http://localhost:3000 redirects. Configure the application's public base URL, trusted proxy/origin settings and OAuth callback registration for the public HTTPS hostname where required. Tailmux does not currently provide automatic public-origin rewriting or eliminate those environment changes.

Publishing a URL makes the selected application reachable beyond your tailnet. Configure provider access controls or application authentication when the preview should be private. Do not publish Orca runtime or other control ports as a web preview.

A stable URL does not guarantee availability: the remote app, SSH connection, Tailmux daemon and tunnel client must all be running. A sleeping laptop breaks this route. For an always-on URL, run the provider connector on the remote box against http://127.0.0.1:3000; that route does not need Tailmux forwarding.

Stop the foreground connector with Ctrl+C, then use tailmux unforward <id> to stop the local forward. A persistent provider route or DNS record remains until removed in that provider. Tailmux does not manage the external connector's lifecycle.

Proposed CLI integration

A future integration could wrap the same workflow:

# Proposed syntax only — not implemented
tailmux forward personal/devbox 3000 --cloudflare
tailmux forward personal/devbox 3000 --cloudflare --hostname preview.example.com
tailmux forward personal/devbox 3000 --ngrok --url https://YOUR-ASSIGNED-DOMAIN.ngrok-free.app

The integration should distinguish temporary previews from saved hostnames, track the connector alongside its forward, and stop it with unforward. Public HTTPS redirect handling needs an explicit external-origin setting. Provider account setup and domain ownership would still be required for stable addresses.

On this page