# Ingress

Make your hub reachable from the internet with Gateway API, Cloudflare Tunnel, port forwarding, or a mesh VPN.

Source: https://railgrid.ai/docs/self-hosting/hub/ingress/


For agents and users to reach your hub, it needs a public URL with TLS. The hub itself is a Pod listening for HTTPS on `:9443`. What gets you from "Pod in a cluster" to `https://console.example.com` depends on your environment, and the same path has to carry more than the console.

## What the ingress must carry

| Hostname | Backend | Protocol at the backend | Notes |
|---|---|---|---|
| `console.example.com` | Hub Service `:9443` | HTTPS | Long-lived connections: MCP streams, event streams, edge tunnels |
| `auth.example.com` | Dex `:5556` | HTTP | Dex answers 404 on `/`; health-check `/healthz` |
| `kcp.example.com`, `kcp-<shard>.example.com` | kcp front proxy and shards `:6443` | HTTPS | [External kcp](https://railgrid.ai/docs/self-hosting/hub/external-kcp/) only; watches are long-lived |
| `*.apps.example.com` | Per-app access gate Services | HTTP | Created by the infrastructure provider as `HTTPRoute`s in tenant namespaces |

Railgrid uses HTTP/1.1 and WebSockets for everything. No gRPC passthrough, no HTTP/2 termination, no ALPN requirements. Any reverse proxy works, as long as it keeps idle connections open long enough (an hour is a safe backend timeout) and forwards the client address.

## Pick the option that fits

| Option | Best for | Needs public IP? |
|:-------|:---------|:-----------------|
| [Gateway API on a cloud load balancer](https://railgrid.ai/docs/self-hosting/hub/gateway-api/) | GKE, EKS, AKS | Yes (provided) |
| [Cloudflare Tunnel](https://railgrid.ai/docs/self-hosting/hub/cloudflare-tunnel/) | Home labs and small clusters, no public IP | No |
| Port forwarding | Static IP, simple | Yes |
| Tailscale / ZeroTier | Private mesh, nobody outside it | No |

Railgrid runs the first two: Gateway API on GKE in production and a Cloudflare Tunnel on a small k3s cluster for development.

## Tell the hub about its proxies

Behind any proxy, set `hub.trustedProxyCIDRs` to the ranges the proxy connects from. The hub keys its pre-authentication rate limits on the client address and only believes `X-Forwarded-For` from a peer inside those ranges. Left empty, every visitor arrives as the proxy's address and shares one bucket, so sign-in throttles for everyone at once.

| Setup | Ranges to list |
|---|---|
| In-cluster ingress controller or cloudflared | The pod CIDR those pods run in, for example `10.42.0.0/16` on k3s |
| GKE Gateway | Google's proxy ranges `35.191.0.0/16` and `130.211.0.0/22`, plus the Gateway's own IP as a `/32`, because the load balancer appends both the client and itself to `X-Forwarded-For` |

Never list ranges that clients connect from; a peer inside them can choose its own bucket.

## Chart objects or your own

The hub chart can render an `Ingress` (`ingress.enabled`) or an `HTTPRoute` (`httpRoute.enabled`) pointing at its own Service. Use them when they fit your controller; the Cloudflare Tunnel page shows both. Disable both and bring your own route when you need something the chart's Service does not carry, such as `appProtocol: HTTPS` for a load balancer that re-encrypts to the backend.

## Port forwarding (static IP)

If your ISP gives you a static public IP and you can forward ports:

1. Forward port 443 on your router to your cluster's ingress controller.
2. Install an ingress controller (nginx, traefik, and so on).
3. Point DNS at your public IP.
4. Issue TLS with cert-manager using an HTTP-01 or DNS-01 challenge.

Example values:

```yaml
hub:
  hubExternalURL: "https://console.example.com"
  trustedProxyCIDRs:
    - 10.244.0.0/16          # the ingress controller's pod CIDR
  tls:
    certManager:
      enabled: true
      issuerRef:
        name: letsencrypt-prod
        kind: ClusterIssuer
      dnsNames:
        - console.example.com

ingress:
  enabled: true
  className: "nginx"
  hosts:
    - host: console.example.com
      paths:
        - path: /
          pathType: Prefix
```

The chart's `tls.certManager` mode issues the certificate straight into the hub's serving Secret. The hub reads that file once at start, so restart the hub after a renewal, or issue the certificate yourself with a long duration as shown in [Install the hub](https://railgrid.ai/docs/self-hosting/hub/helm/#serving-certificate-and-provider-trust).

## Tailscale / ZeroTier (private mesh)

If you do not want a public-facing hub, run a mesh VPN.

1. Install Tailscale or ZeroTier on the hub's node.
2. Install it on every machine that needs to reach the hub: CLI users and edge agents.
3. Use the mesh IP or MagicDNS hostname as `hub.hubExternalURL`.

This works well when everyone is on the same mesh; less convenient if you need to onboard people outside it, and published applications are not reachable from the public internet.

## Next

- [Gateway API on GKE](https://railgrid.ai/docs/self-hosting/hub/gateway-api/)
- [Cloudflare Tunnel walkthrough](https://railgrid.ai/docs/self-hosting/hub/cloudflare-tunnel/)
- [Authentication](https://railgrid.ai/docs/self-hosting/hub/authentication/): pick an auth method after you have decided how to expose the hub
