Documentation

Getting started

Everything you need to put a local port on a public URL. Install once, then start a tunnel, pin a stable URL, or watch requests live. Every command below is copy-paste ready.

Install

viaduct needs Python 3.11 or newer and pipx, which keeps the CLI in its own isolated environment and on your PATH.

$ pipx install git+https://github.com/webmull/viaduct

That installs both the viaduct client and the viaductd server. Confirm it is on your path:

$ viaduct --version

Start a tunnel

Point viaduct http at any local port. It prints a public HTTPS URL and forwards traffic to that port until you stop it.

  1. Run your app on some local port, for example a dev server on 8080.
  2. Open the tunnel with the command below.
  3. Share the printed URL. Every request to it rides the tunnel to your localhost.
$ viaduct http 8080
bash
 tunnel live

  https://sunny-otter.viaduct.sh
  → http://localhost:8080
  press q to stop
Nothing listening yet? viaduct still opens the tunnel and serves a friendly 502 page until your app comes up, so you can start them in either order.

Stop the tunnel with q or Ctrl+C. On a drop it reconnects on its own, and by default each reconnect gets a fresh URL. To keep the same one, pin it.

Keep the same URL (--pin)

Add --pin to keep the same public URL for a given port across reconnects and restarts. Useful for a webhook endpoint you register once, or a demo link you want to stay put.

$ viaduct http 8080 --pin
bash
 tunnel live

  https://amber-stoat-cfb1.viaduct.sh
  → http://localhost:8080
  press q to stop
The name is still assigned by the server. You cannot choose the string, so no squatting on someone else's brand. It is derived from a random secret stored once at ~/.config/viaduct/pin.key. Different ports get different stable names; delete that file to rotate to a new one.

Inspect requests (--inspect)

Add --inspect to print each request as it happens: method, path, status, and timing. Ideal for debugging webhooks without leaving your terminal.

$ viaduct http 8080 --inspect
bash
 tunnel live

  https://funny-otter.viaduct.sh
  → http://localhost:8080

14:02:01  GET  /                       200 5ms
14:02:04  POST /webhooks/stripe        200 61ms
14:02:06  GET  /favicon.ico            404 1ms
14:02:09  GET  /ws                     101 3ms

Combine flags

Flags stack. Pin a stable URL and watch its traffic at the same time:

$ viaduct http 8080 --pin --inspect

The order does not matter, and the local port can be any port your app listens on.

Custom domains (bring your own)

Serve a tunnel on your own domain with a single CNAME. There's no extra flag, the mapping lives in your DNS.

  1. Pin the tunnel so it has a stable name: viaduct http 8080 --pin gives you something like funny-otter-9a1c2b3d4e5f.viaduct.sh.
  2. Add a CNAME at your registrar (the exact screen differs per provider): point demo.example.com at that funny-otter-9a1c2b3d4e5f.viaduct.sh name.
  3. Open it. The first request auto-provisions an HTTPS certificate, then https://demo.example.com serves your local app.
Why a CNAME, not an A record? viaductd follows the CNAME back to your tunnel to know where to route, so it has to point at the *.viaduct.sh name rather than straight at an IP. And because the certificate is only issued once your domain already points here, you can only serve domains whose DNS you actually control.

Regions

viaduct.sh operates tunnel nodes in four regions. Run your tunnel on the one nearest your traffic with --region; with no flag you get London by default.

Region--regionTunnel URL
London (default)lonname.viaduct.sh
New Yorknycname.nyc.viaduct.sh
Singaporesgname.sg.viaduct.sh
Sydneysydname.syd.viaduct.sh
$ viaduct http 8080 --region nyc   # -> name.nyc.viaduct.sh
$ viaduct http 8080 --region sg    # -> name.sg.viaduct.sh

List them any time with viaduct regions.

On latency: a tunnel always ends at your machine, so a region mainly helps when your visitors and your machine are both far from the default. Pick the node nearest your traffic; there's no benefit to switching if London is already close.

All options

Every flag on viaduct http:

OptionDefaultDescription
--server host:portviaduct.sh:4443The viaductd server to dial.
--region lon|nyc|sg|sydnonePick a server region; a shortcut for --server.
--pinoffKeep the same public URL across reconnects (stable subdomain).
--inspectoffLog each request: method, path, status, and time.
--pool-size N40Idle data connections kept ready for incoming requests.
--tls / --no-tlson, off for localhostTLS to the tunnel port.
--tls-ca PATHnoneExtra CA bundle to trust (for a self-signed server).

Flags can also live in ~/.config/viaduct/config.toml instead of the command line:

server = "viaduct.sh:4443"
tls    = true

Stop and update

Stop a running tunnel with q or Ctrl+C. It drains in-flight requests before exiting.

Update to the latest release at any time:

$ viaduct upgrade

viaduct upgrade follows the stable release channel. On an interactive terminal the client also shows a one-line notice, at most once a day, when a newer release exists.

Run your own server

The client defaults to the hosted viaduct.sh, but the point of viaduct is that you run your own. One Ubuntu droplet runs Caddy (public HTTPS) and viaductd, and one script sets it all up. Then point the client at it:

$ viaduct http 8080 --server your-domain.com:4443