AnkraDocs
Console

Concepts

Tunnels

Publish an HTTP service that has only outbound connectivity, with a public hostname on IPv4 and IPv6, a certificate and the zone's WAF, through a connector you run next to it.

A tunnel gives a service that nothing on the internet can reach a public HTTPS hostname: a pod on an IPv6-only cluster, a VM behind NAT, a laptop. You run a small connector next to the service. It dials the Ankra relay out over TLS, and the relay answers your hostnames on IPv4 and IPv6, terminates TLS with a certificate it obtains for each hostname, inspects every request with your zone's WAF and sends it through the connector to the origin. No inbound port, public address or load balancer is needed.

Creating one#

Every hostname sits in one of your verified hosted DNS zones, and names the origin URL as the connector reaches it:

bash
curl -X POST https://cloud.ankra.app/v1/tunnels \
  -H "Authorization: Bearer $ANKRA_CLOUD_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{ "name": "production",
        "hostnames": [ { "hostname": "app.example.com", "origin_url": "http://web.default.svc.cluster.local:8080" } ] }'

The answer carries the tunnel and its connector token (aktn_…). It is shown this once: Ankra keeps only its SHA-256. Store it as a secret, and rotate it if it leaks.

Ankra publishes the relay's A and AAAA records at each hostname in your zone and applies the zone's WAF policy (DNS → zone → WAF). A hostname has one owner: a tunnel cannot take a name that holds an A, AAAA or CNAME record (delete it first) or a Proxied record, and while a tunnel holds a name you cannot add such a record there.

Running the connector#

The connector needs the relay address and the token, nothing else: it learns its hostnames and origins from the tunnel. The console shows both, with the published image, under Networking → Tunnels → your tunnel.

With Docker, on any host that reaches the origin:

bash
docker run -d --name ankra-tunnel --restart unless-stopped \
  -e ANKRA_TUNNEL_RELAY=<relay address> \
  -e ANKRA_TUNNEL_TOKEN=<token> \
  <connector image>

On Kubernetes, run it as a Deployment with the token in a Secret (encrypt it before it enters Git, for example with ankra cluster encrypt). Two replicas keep the tunnel up while one restarts:

yaml
apiVersion: v1
kind: Secret
metadata: { name: ankra-tunnel-token, namespace: ankra-tunnel }
stringData: { token: <token> }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: ankra-tunnel, namespace: ankra-tunnel }
spec:
  replicas: 2
  selector: { matchLabels: { app.kubernetes.io/name: ankra-tunnel } }
  template:
    metadata: { labels: { app.kubernetes.io/name: ankra-tunnel } }
    spec:
      containers:
        - name: ankra-tunnel
          image: <connector image>
          env:
            - { name: ANKRA_TUNNEL_RELAY, value: <relay address> }
            - { name: ANKRA_TUNNEL_TOKEN_FILE, value: /etc/ankra-tunnel/token }
          volumeMounts: [ { name: token, mountPath: /etc/ankra-tunnel, readOnly: true } ]
          readinessProbe: { httpGet: { path: /ready, port: 2000 } }
      volumes: [ { name: token, secret: { secretName: ankra-tunnel-token } } ]

The origin sees the public hostname as Host, X-Forwarded-Proto: https and the client's address in X-Forwarded-For, so an ingress behind the connector routes as it would for direct traffic. Point origin_url at the ingress controller's Service to publish several applications through one tunnel.

Status#

A tunnel is connected while the relay holds at least one of its connectors (it lists them with where they connected from), disconnected once connectors were seen and are gone, and inactive before the first connector connected. The first HTTPS request to a new hostname waits a few seconds while its certificate is issued.

Changing hostnames#

PATCH /v1/tunnels/{id} with the complete hostnames list replaces them. Running connectors reconnect within seconds and serve the new list; removed hostnames stop answering and their names fall back to your zone's own records.

Rotating the token#

POST /v1/tunnels/{id}/token/rotate answers a new token once. Sessions opened with the old token end within seconds, so update the connector's secret right away. Deleting the tunnel ends its sessions the same way.

Limits#

  • HTTP, HTTPS and WebSockets (with a connector from October 2026 or later); no raw TCP or UDP. A WebSocket closes after 10 minutes without traffic and 24 hours at most, and one tunnel may hold 1024 open at once.
  • Request bodies up to 8 MiB.
  • Up to 50 hostnames per tunnel and 100 tunnels per account; wildcard hostnames are not supported.
  • Hostnames must be in a hosted DNS zone of your account; zones served elsewhere cannot be used yet.