The path gateway

The path gateway is a built-in HTTP reverse proxy inside telad. It exposes one tunnel port and routes incoming HTTP requests to different local services based on URL path prefix, eliminating the need for a separate nginx, Caddy, or Traefik instance for tunnel-internal routing.

When to use a gateway

Use a gateway when you have several HTTP services on one machine and want to reach all of them through a single tunnel port. Common examples:

  • A web frontend, a REST API, and a metrics endpoint running on the same host
  • A multi-page web app with backend services on different ports
  • A development stack you want accessible through one URL

You do not need a gateway when you have only one HTTP service (just expose it as a normal service), when your services use TCP rather than HTTP (expose them as normal TCP services), or when you already use a reverse proxy in front of your services and want to keep it as the edge.

How it works

Without a gateway, a client connecting to a multi-service application gets one binding per service port:

localhost:3000   → port 3000
localhost:4000   → port 4000
localhost:4100   → port 4100

The browser opens http://localhost:3000 and calls the API on a different origin (localhost:4000). Same host, different port -- that is still a cross-origin request under browser CORS rules, which means either CORS headers on the API server, a hardcoded API URL in the UI code, or an extra proxy layer somewhere.

With a gateway, the client gets one binding:

localhost:8080   → HTTP

The browser opens http://localhost:8080/. The UI calls /api/users. The gateway sees the /api/ prefix and proxies the request to the local API service. Same origin. No CORS. No extra configuration.

Configuration

Gateway configuration lives in telad.yaml under each machine, alongside the services: list:

hub: wss://your-hub.example.com
token: "<your-agent-token>"

machines:
  - name: barn
    services:
      - port: 5432
        name: postgres
        proto: tcp
    gateway:
      port: 8080
      routes:
        - path: /api/
          target: 4000
        - path: /metrics/
          target: 4100
        - path: /
          target: 3000

This declares one direct TCP service (PostgreSQL on port 5432, exposed through the tunnel as usual) and a gateway listening on port 8080 with three routes. The HTTP services on ports 3000, 4000, and 4100 are not in the services: list -- they are private to the machine and reachable only through the gateway. The tunnel exposes port 8080 and port 5432.

Field reference

FieldRequiredDescription
gateway.portYesPort the gateway listens on inside the WireGuard tunnel. Does not need to match any local service port.
gateway.routesYesList of routes, each mapping a URL path prefix to a local target port.
routes[].pathYesURL path prefix to match (e.g. /api/, /admin/, /).
routes[].targetYesLocal TCP port to proxy matched requests to.

Route matching

Routes are matched by longest path prefix first. The order in the YAML file does not matter; telad sorts them at startup. A route with path: / matches any request not claimed by a more specific route.

With these routes:

routes:
  - path: /
    target: 3000
  - path: /api/v2/
    target: 4002
  - path: /api/
    target: 4000

A request to /api/v2/users matches /api/v2/ (target 4002). A request to /api/health matches /api/ (target 4000). A request to /about matches / (target 3000).

Connecting through a gateway

The gateway appears to clients as a service named gateway. Use it in a connection profile like any other service:

# ~/.tela/profiles/barn.yaml
connections:
  - hub: wss://your-hub.example.com
    machine: barn
    services:
      - name: gateway
      - name: postgres
tela connect -profile barn

Output:

Services available:
  localhost:8080   → HTTP
  localhost:5432   → port 5432

Port labels come from the well-known port table (22=SSH, 80/8080=HTTP, 3389=RDP, etc.). Ports not in the table show as port N.

If port 8080 conflicts with something local, override it:

services:
  - name: gateway
    local: 18080

Direct access alongside the gateway

You can expose a service both through the gateway (for browser access) and as a direct service (for tools like curl or Postman). Add it to the agent's services: list as well as the gateway routes, then include it in the profile:

# telad.yaml
machines:
  - name: barn
    services:
      - port: 4000
        name: api
        proto: http
    gateway:
      port: 8080
      routes:
        - path: /api/
          target: 4000
        - path: /
          target: 3000
# profile
connections:
  - hub: wss://your-hub.example.com
    machine: barn
    services:
      - name: gateway
      - name: api
        local: 14000

Now http://localhost:8080/api/... reaches the API through the gateway, and http://localhost:14000/... reaches it directly.

Cross-environment use

When you maintain the same application across several environments, each running its own telad, a profile can connect to multiple gateways simultaneously:

connections:
  - hub: wss://prod-hub.example.com
    machine: app
    services:
      - name: gateway

  - hub: wss://staging-hub.example.com
    machine: app
    services:
      - name: gateway
        local: 18080

When connecting to both environments simultaneously, use local: overrides to put them on different ports. Without an override, both gateways would try to bind localhost:8080 and the second would fall back to localhost:18080. Making it explicit avoids relying on fallback behavior. The routing logic stays in each environment's telad.yaml, not in the client profile.

Limitations

The gateway does not terminate TLS (the WireGuard tunnel already provides end-to-end encryption). It does not authenticate users (that is the hub's token and ACL layer). It does not load-balance across instances. It does not proxy WebSocket connections -- if you need WebSocket access to a service, expose it as a separate service alongside the gateway. It is not a replacement for an internet-facing reverse proxy with TLS termination, rate limiting, or WAF rules.

For the design rationale and the relationship between the path gateway and the other gateway primitives in Tela, see Gateways in the Design Rationale section. For a step-by-step setup walkthrough and troubleshooting, see Set up a path-based gateway.