Self-hosting the tunnel relay
Run the mcpg-cloud-relay broker inside your own trust boundary — listeners, authentication (static tokens or control-plane entitlements), TLS and DNS, rate limits, and how tunnelling gateways dial in.
The relay (mcpg-cloud-relay) is the broker that answers a
reverse tunnel: self-hosted gateways dial out to it,
and it host-routes inbound MCP requests back down the owning tunnel. The managed
relay on mcpg.cloud is the zero-ops option; run your own when
you want the relay inside your own trust boundary — the honest way to keep
the relay from seeing anything you don't want it to (see the e2ee note in the
tunnels guide).
This guide is operator-facing: the listeners, how to authenticate dial-ins, TLS and DNS, and how gateways point at your relay.
What it runs
The relay is a single binary with four listeners:
self-hosted gateway ──dials out (wss)──▶ :7845 agent-accept
│ registers the tunnel
MCP client ──────────https──────────────▶ :8080 public MCP (host-routed)
same-org gateway ──/federate/<name>──────▶ :8081 federation ingress
ops / probes ───────────────────────────▶ :9090 /healthz /readyz /metrics
- Agent-accept (
:7845) — where gateways dial in. Authenticated; TLS when configured. - Public MCP (
:8080) — public tunnels are reachable here, host-routed by the first DNS label (<id>.<zone>). - Federation ingress (
:8081) — same-org gateways reach a private tunnel by name for reverse federation. - Ops (
:9090) — health, readiness, and Prometheus metrics.
Authentication (fail-closed)
The relay refuses to boot without an authenticator — a dial-in is only registered after it's authorized. Pick one:
Static token grants (simple self-host)
Map bearer tokens to orgs. Each dialing gateway presents its token in
MCPG_TUNNEL_TOKEN.
MCPG_RELAY_AUTH_TOKENS="tok_acme:acme,tok_globex:globex" \
mcpg-cloud-relay
Control-plane entitlements (managed posture)
Point the relay at a control plane and it authorizes every dial-in against the org's plan — resolution, suspension, allowed exposure/trust modes, and the concurrent-tunnel quota — and meters relayed bytes. This is what the managed service runs.
MCPG_RELAY_CP_URL=https://cp.internal.example.com \
MCPG_RELAY_CP_TOKEN=$SHARED_RELAY_CP_TOKEN \
mcpg-cloud-relay
MCPG_RELAY_CP_TOKEN must equal the control plane's MCPG_CP_TUNNEL_API_TOKEN.
When MCPG_RELAY_CP_URL is set, the CP is authoritative and takes precedence
over a static token map.
Insecure (local dev only)
MCPG_RELAY_AUTH=insecure-allow-all MCPG_RELAY_INSECURE_ORG=dev mcpg-cloud-relay
Accepts every dial-in and attributes it to one org. Never in production.
TLS and DNS
-
Agent-accept TLS. Provide a cert + key so the tunnel backbone is encrypted end to end into the relay. Absent, the agent-accept listener is plaintext (valid only behind a TLS-terminating load balancer).
bashMCPG_RELAY_TLS_CERT=/etc/relay/tls.crt \ MCPG_RELAY_TLS_KEY=/etc/relay/tls.key \ mcpg-cloud-relay -
Public tunnel zone. Public tunnels are minted at
https://<id>.<zone>/mcp, where the zone defaults totunnels.mcpg.cloud. Set your own and put a wildcard TLS cert for*.<zone>on the public listener (typically at your ingress/load balancer):bashMCPG_RELAY_ZONE=tunnels.example.com mcpg-cloud-relayPoint a wildcard DNS record
*.tunnels.example.comat the public listener. Private tunnels mint no hostname, so they need no DNS.
Rate limits and timeouts
Edge protection is on by default; tune per deployment:
| Env var | Default | Purpose |
|---|---|---|
MCPG_RELAY_IP_RATE / MCPG_RELAY_IP_BURST | 50 / 100 | Per-IP token bucket on the public edge |
MCPG_RELAY_TUNNEL_RATE / MCPG_RELAY_TUNNEL_BURST | 200 / 400 | Per-tunnel token bucket |
MCPG_RELAY_FORWARD_TIMEOUT_SECS | 30 | Wait for a response head before 504 (the streamed SSE body is not bounded by this) |
Pointing gateways at your relay
On each tunnelling gateway, override the relay endpoint and, if your relay uses a private CA, trust it:
export MCPG_TUNNEL_TOKEN=tok_acme # matches your relay's grant
export MCPG_TUNNEL_CA=/etc/mcpg/relay-ca.pem # only if the relay uses a private CA
mcpg --config gw.yaml --tunnel wss://relay.example.com:7845
(--tunnel <wss-url> is shorthand for --tunnel --tunnel-relay <wss-url>.)
For a gateway that federates a private tunnel, point its
gateway.server.tunnel_federation.relay_ingress_url at your relay's federation
ingress (https://relay.example.com:8081). See the
reverse-federation walkthrough.
Full environment reference
| Env var | Flag | Default | Purpose |
|---|---|---|---|
MCPG_RELAY_AGENT_ADDR | --agent-addr | 0.0.0.0:7845 | Agent-accept listener |
MCPG_RELAY_PUBLIC_ADDR | --public-addr | 0.0.0.0:8080 | Public MCP listener |
MCPG_RELAY_FEDERATION_ADDR | --federation-addr | 0.0.0.0:8081 | Federation ingress |
MCPG_RELAY_OPS_ADDR | --ops-addr | 0.0.0.0:9090 | Health / readiness / metrics |
MCPG_RELAY_ZONE | --zone | tunnels.mcpg.cloud | Public URL zone (<id>.<zone>) |
MCPG_RELAY_JSON_LOGS | --json-logs | off | JSON log output |
MCPG_RELAY_AUTH_TOKENS | --auth-tokens | — | Static token:org[,…] grants |
MCPG_RELAY_AUTH | --auth-mode | — | insecure-allow-all (dev only) |
MCPG_RELAY_INSECURE_ORG | --insecure-org | dev | Org under insecure-allow-all |
MCPG_RELAY_CP_URL | --cp-url | — | Control-plane URL (entitlement-aware auth) |
MCPG_RELAY_CP_TOKEN | --cp-token | — | Bearer to the CP tunnel API (required with cp-url) |
MCPG_RELAY_TLS_CERT | --tls-cert | — | Agent-accept TLS cert chain |
MCPG_RELAY_TLS_KEY | --tls-key | — | Agent-accept TLS key |
MCPG_RELAY_IP_RATE / _IP_BURST | --ip-rate / --ip-burst | 50 / 100 | Per-IP edge limit |
MCPG_RELAY_TUNNEL_RATE / _TUNNEL_BURST | --tunnel-rate / --tunnel-burst | 200 / 400 | Per-tunnel limit |
MCPG_RELAY_FORWARD_TIMEOUT_SECS | --forward-timeout-secs | 30 | Response-head timeout |
High availability
The relay's tunnel registry is in memory, per replica: a tunnel is registered on the instance it dialed into, and the public/federation route resolves on that same instance. Running multiple replicas therefore needs sticky routing — a dialing gateway and the clients reaching its tunnel must land on the same replica (session affinity at the load balancer, or one relay per zone shard). Until shared-registry routing lands, size a single relay for your tunnel fan-out and scale by sharding zones, not by round-robin replicas.
Security checklist
- An authenticator is configured (static tokens or CP) — the relay won't boot otherwise.
- Agent-accept TLS is enabled, or a trusted proxy terminates TLS in front of it.
- A wildcard cert covers
*.<zone>on the public listener (if you allow public tunnels). - The ops listener (
:9090) is not exposed to the internet. - Rate limits are sized for your edge.
- Gateways trust the relay's CA (
MCPG_TUNNEL_CA) if it isn't publicly rooted.
See also
- Reverse tunnels — the gateway side, exposure/trust modes, and the reverse-federation pairing.
- MCP federation — the consuming side of a
tunnel://upstream. - Managed tunnels & plans — the hosted relay, quota, and billing.