Client

Tunnel Templates

Set up tunnel templates in the Portr CLI to save and reuse tunnel settings, so you can start multiple named tunnels with a single command.

Why Use Templates?

Templates provide several benefits:

  • Run multiple tunnels simultaneously
  • Reuse common configurations for frequently used subdomains and ports
  • Quick startup with simple commands
  • Consistent setups across different environments

Setting Up Templates

Open the config file

Open the portr client config file by running:

portr config edit

Configure templates

This will open a file with contents similar to:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

Disable TUI (Terminal User Interface)

You can disable the interactive terminal interface by adding disable_tui: true to your configuration:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
disable_tui: true
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

When TUI is disabled, the client will:

  • Run in headless mode without the interactive interface
  • Log connection events, errors, and HTTP requests directly to the console
  • Show tunnel status updates and health check results
  • Exit gracefully on errors

QR Code for the Tunnel URL

The terminal interface can show a QR code for the tunnel URL, so you can open it on a phone without typing it. Press r to show or hide it. This is on by default; nothing is rendered until you press the key.

Set enable_qr_code: false to remove the toggle and its footer hint:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
enable_qr_code: false
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

The panel renders only when exactly one HTTP or stub tunnel is running, since there is no way to pick between several. TCP tunnels have no browsable URL and are ignored. The panel takes vertical space from the request table while visible, and is replaced by a note if the terminal is too narrow to render a scannable code.

This option has no effect when disable_tui: true, because there is no terminal interface to render the code into.

Configure or Disable the Local Dashboard

You can change the inspector port or disable it permanently in the client config:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
dashboard_port: 8888
disable_dashboard: false
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

Or disable the dashboard entirely:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
disable_dashboard: true
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

disable_dashboard takes precedence over dashboard_port, so a disabled dashboard stays disabled until you change the config itself.

Disable Update Checks

You can disable automatic update checks and notifications by adding disable_update_check: true:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
disable_update_check: true
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

When update checks are disabled, the client will:

  • Skip checking for new versions on startup
  • Not display update notifications
  • Reduce startup time and network requests

Auto-Clear Connection Logs

You can automatically clear old connection logs by adding connection_log_retention_days:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
connection_log_retention_days: 7
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

When connection_log_retention_days is set to:

  • 0 (default): automatic cleanup is disabled
  • > 0: logs older than the configured number of days are removed

Redact Logged Headers

Portr redacts common sensitive headers in newly captured request logs by default. Customize the list with redact_headers:

redact_headers:
  - Authorization
  - Cookie
  - Set-Cookie
  - Proxy-Authorization

Add more templates

You can create additional tunnel templates under the tunnels key:

tunnels:
  - name: portr
    subdomain: portr
    port: 4321
  - name: pg
    subdomain: postgres-dev
    port: 5432
    type: tcp
  - name: api
    subdomain: api-dev
    port: 3000
    type: http
  - name: yaml
    type: stub
    subdomain: yaml
    response_format: application/yml
    response_tmpl_file: ./response.yml

Using Templates

Start individual tunnels

Start specific tunnels by name:

portr start portr

Start multiple tunnels

Start multiple services at once:

portr start portr pg api

Start all tunnels

Start all configured tunnels:

portr start

Start a group

Name a set of tunnels with groups, then start the whole set with one word:

tunnels:
  - name: web
    port: 3000
  - name: api
    port: 8000
  - name: pg
    type: tcp
    port: 5432

groups:
  frontend: [web, api]
portr start frontend
portr start frontend pg

A group name must not match a tunnel name, and every member must be the name of an existing tunnel.

For more details about available commands and options, run portr --help.

Team Templates

Instead of every member writing the same tunnels by hand, a team admin can publish one template from the admin dashboard under Client template. It holds the same tunnels and groups you would write locally:

tunnels:
  - name: web
    subdomain: acme-web
    port: 3000
  - name: api
    subdomain: acme-api
    port: 8000

groups:
  frontend: [web, api]

Only tunnels and groups can be set in a team template. server_url, ssh_url, secret_key and machine-local settings such as dashboard_port stay yours; the server rejects them at save time.

Anything that names a path on one machine is rejected too, since a template is shared verbatim: static tunnels (type: static, which serves a local dir) and response_tmpl_file. Use an inline response_tmpl for stub tunnels.

New members get the template on a fresh setup:

portr auth set --token {your_token} --remote {your_domain}

If you already have a config file, portr auth set only refreshes the token and urls. Pull the template explicitly:

portr config pull

portr config pull replaces the local tunnels and groups with the team template and leaves everything else in the file — including your comments and secret key — untouched.

portr config pull overwrites tunnels you added by hand. Copy anything you want to keep into the team template first, or re-add it after pulling.

Configuration Options

Global Configuration Options

These options apply to the entire client configuration:

  • server_url: The Portr server URL
  • ssh_url: The SSH server URL for tunnel connections
  • secret_key: Your authentication secret key
  • groups: Named sets of tunnel names; portr start <group> starts every tunnel in the set
  • disable_tui: Disable the interactive terminal interface (default: false)
  • enable_qr_code: Offer a QR code toggle (press r) in the terminal interface for the tunnel URL (default: true). The code is only rendered once the key is pressed. Ignored when disable_tui is true.
  • dashboard_port: Local port for the inspector dashboard (default: 7777)
  • disable_dashboard: Disable the local inspector dashboard completely (default: false)
  • disable_update_check: Disable automatic update checks and notifications (default: false)
  • enable_request_logging: Persist local HTTP and WebSocket inspector logs for portr logs, replay, and the dashboard (default: true)
  • redact_headers: Header names whose values are replaced with [redacted] in newly captured logs
  • connection_log_retention_days: Auto-delete connection logs older than N days (default: 0, disabled)
  • health_check_interval: Health check interval in seconds (default: 3)
  • health_check_max_retries: Maximum health check retry attempts (default: 10)
  • insecure_skip_host_key_verification: Skip SSH host key verification (default: true)

Stub Tunnel Options

These options apply to stub tunnel entries with type: stub:

  • subdomain: Public subdomain that serves the stub response
  • response_format: Response Content-Type, such as application/json or application/yml
  • response_tmpl: Inline response template
  • response_tmpl_file: Path to a response template file, resolved relative to the config file

Use exactly one of response_tmpl or response_tmpl_file.

SSH Host Key Verification

By default, the Portr client skips SSH host key verification for ease of use. You can enable host key verification for additional security:

server_url: example.com
ssh_url: example.com:2222
secret_key: { your-secret-key }
insecure_skip_host_key_verification: false
tunnels:
  - name: portr
    subdomain: portr
    port: 4321

When host key verification is enabled:

  • On first connection, the server's host key is automatically saved to ~/.portr/known_hosts (Trust On First Use)
  • Subsequent connections verify the server's key matches the saved key
  • If the key changes unexpectedly, the connection fails with a warning (potential security issue)

If you enable host key verification, ensure your server persists SSH host keys across restarts using the PORTR_SSH_KEYS_DIR environment variable. Otherwise, clients will fail to connect after each server restart.

Tunnel Template Options

Each tunnel template supports the following options:

  • name: A unique identifier for the tunnel
  • subdomain: The subdomain to use for the tunnel
  • port: The local port to tunnel
  • type: The tunnel type (http, tcp, stub, or static)
  • host: The local host to bind to (default: localhost)
  • dir: Static only. Directory to serve, resolved relative to the config file
  • pool_size: HTTP only. Number of SSH workers to run per HTTP tunnel (default: 2). Increases resilience and throughput.
  • host_header: HTTP only. Host header sent to the local server. Use rewrite for the local address, or any literal hostname (default: pass the public host through)

HTTP tunnels use streaming reverse proxying by default. Request and response bodies are forwarded as they arrive, while inspector captures are stored asynchronously and capped at 1 MiB per body. The former enable_http_reverse_proxy option is no longer required; existing configurations containing it still load, but the option is ignored.

Example Configurations

Development Environment

tunnels:
  - name: frontend
    subdomain: app-dev
    port: 3000
    type: http
  - name: backend
    subdomain: api-dev
    port: 8000
    type: http
    pool_size: 2
  - name: database
    subdomain: db-dev
    port: 5432
    type: tcp

Testing Environment

tunnels:
  - name: staging
    subdomain: staging
    port: 3000
    type: http
  - name: webhook-test
    subdomain: webhooks
    port: 4000
    type: http