Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/outray-tunnel/outray/llms.txt

Use this file to discover all available pages before exploring further.

The OutRay CLI reads a TOML file to start multiple tunnels at once. By default it looks for outray/config.toml in the current working directory. Pass --config <path> to outray start or outray validate-config to use a different path.
outray start --config ./config/tunnels.toml

File structure

A config file has an optional [global] section and one or more [tunnel.<name>] sections.
[global]
org = "my-team"
# server_url = "wss://api.outray.dev/"

[tunnel.web]
protocol = "http"
local_port = 3000
local_host = "localhost"
subdomain = "my-app"
custom_domain = "app.example.com"

[tunnel.api]
protocol = "http"
local_port = 8000
subdomain = "api"

[tunnel.postgres]
protocol = "tcp"
local_port = 5432
local_host = "localhost"
remote_port = 20000

[tunnel.game-server]
protocol = "udp"
local_port = 5000
remote_port = 30000

[global]

Global settings that apply to all tunnels unless overridden at the tunnel level.
org
string
Default organization slug. Every tunnel uses this org unless it specifies its own org field.
[global]
org = "my-team"
server_url
string
WebSocket URL of the OutRay tunnel server. Must use the ws:// or wss:// scheme. Omit this field to use the production server at wss://api.outray.dev/.
[global]
server_url = "wss://api.outray.dev/"
Set server_url to a ws:// address when running a self-hosted OutRay server on your own infrastructure.

[tunnel.<name>]

Each tunnel section defines one tunnel. Replace <name> with a descriptive identifier, for example [tunnel.web] or [tunnel.postgres]. Names must be unique within the file.

Connection fields

protocol
string
required
The tunnel protocol. Must be one of "http", "tcp", or "udp".
protocol = "http"
local_port
number
required
The local port that OutRay will forward traffic to. Must be an integer between 1 and 65535.
local_port = 3000
local_host
string
default:"localhost"
Hostname or IP address of the local service. Useful when the service is running in another container or on a different machine on your LAN.
local_host = "localhost"

HTTP-only fields

subdomain, custom_domain, and password are only valid when protocol = "http". Including them with tcp or udp tunnels causes a validation error.
subdomain
string
Request a specific subdomain for the public tunnel URL, for example my-app produces https://my-app.tunnel.outray.app. Requires authentication.
subdomain = "my-app"
custom_domain
string
Use a custom domain instead of a generated subdomain. The domain must be configured in the OutRay dashboard and your DNS must point to the OutRay server before the tunnel can serve traffic.
custom_domain = "app.example.com"
password
string
Protect the HTTP tunnel with Basic Auth. Visitors are prompted for this password before they can access the tunnel.
password = "s3cr3t"
local
boolean
default:"false"
When true, the service is also advertised on the local network via mDNS so devices on the same LAN can reach it at <subdomain>.local in addition to the remote tunnel URL.
local = true

TCP/UDP-only fields

remote_port is only valid when protocol = "tcp" or protocol = "udp". It is not allowed for HTTP tunnels.
remote_port
number
Request a specific public port. TCP ports are allocated from the range 20000–30000; UDP ports from 30001–40000. If the requested port is already in use the server assigns a different one.
remote_port = 20000

Organization override

org
string
Organization slug for this tunnel only. Overrides global.org. Useful when tunnels in the same config file belong to different organizations.
org = "other-team"

Annotated example

# outray/config.toml

[global]
# Default org for all tunnels in this file.
org = "my-team"

# Uncomment to use a self-hosted server.
# server_url = "wss://tunnel.internal.example.com/"


# ── HTTP tunnel ────────────────────────────────────────────
[tunnel.web]
protocol      = "http"
local_port    = 3000
local_host    = "localhost"     # optional, default is "localhost"
subdomain     = "my-app"        # requests https://my-app.tunnel.outray.app
custom_domain = "app.example.com"  # overrides the subdomain URL
password      = "s3cr3t"        # HTTP Basic Auth


# ── Second HTTP tunnel (minimal) ───────────────────────────
[tunnel.api]
protocol   = "http"
local_port = 8000
subdomain  = "api"


# ── TCP tunnel ─────────────────────────────────────────────
[tunnel.postgres]
protocol    = "tcp"
local_port  = 5432
local_host  = "localhost"
remote_port = 20000             # request TCP port 20000 (20000–30000 range)


# ── UDP tunnel ─────────────────────────────────────────────
[tunnel.game-server]
protocol    = "udp"
local_port  = 5000
remote_port = 30000             # request UDP port 30000 (30001–40000 range)

Validation

Run outray validate-config to check your configuration file before starting tunnels:
outray validate-config

# ✓ Config file is valid
#
# Found 4 tunnel(s):
#
#   [web]
#     Protocol: http
#     Local: localhost:3000
#     Subdomain: my-app
#     Custom Domain: app.example.com
#
#   [api]
#     Protocol: http
#     Local: localhost:8000
#     Subdomain: api
#
#   [postgres]
#     Protocol: tcp
#     Local: localhost:5432
#     Remote Port: 20000
#
#   [game-server]
#     Protocol: udp
#     Local: localhost:5000
#     Remote Port: 30000
The validator enforces:
  • At least one [tunnel.*] section must be present.
  • protocol must be "http", "tcp", or "udp".
  • local_port must be an integer between 1 and 65535.
  • local_host must be a valid hostname when provided.
  • server_url must be a valid WebSocket URL (ws:// or wss://) when provided.
  • remote_port is forbidden on HTTP tunnels.
  • subdomain and custom_domain are forbidden on TCP and UDP tunnels.