Skip to main content

Know when the tunnel can carry traffic

Starting the tunnel process is not the same as the tunnel being able to forward requests. A remote tunnel server has to be provisioned, the SSH connection established and a self-test passed. Health checks let your CI job or orchestrator wait for exactly that moment, and notice when it is lost.

  • Since v5.0
  • Docker HEALTHCHECK
  • Kubernetes probes

Endpoints

Served on the metrics port, 8003 by default (change with --metrics-port).

Path Answers Use it for
/healthz 200 {"status":"ok"} whenever the process is answering, including mid-reconnect Liveness: restart the tunnel only if this fails
/readyz 200 when the tunnel is forwarding, 503 otherwise Readiness: start tests, or route traffic, only when this is 200
/ JSON status: version, uptime, requests, bytes transferred Dashboards and quick inspection
/metrics Prometheus exposition Monitoring
curl -i http://127.0.0.1:8003/readyz
  • /readyz turns 200 only after the startup self-test passes, and after a reconnect only once both forwarding directions are back.
  • During a reconnect, /healthz stays 200 while /readyz returns 503. The tunnel is recovering by itself, so it should be taken out of rotation, not restarted.
  • The health endpoints are deliberately not behind --metrics-auth. Container probes have no good way to carry credentials, and the endpoints disclose nothing beyond up or down.
  • Like every local listener, the metrics port binds 127.0.0.1 unless you set --bind-address 0.0.0.0.

The --ready command

Asks a running tunnel whether it is ready, then exits 0 (ready) or 1 (not ready, or nothing listening). No curl or wget needed.

java -jar testingbot-tunnel.jar --ready
echo $?   # 0 when ready

# a tunnel started with --metrics-port 9003
java -jar testingbot-tunnel.jar --ready --metrics-port 9003

--ready queries /readyz on --metrics-port, so pass the same port you started the tunnel with.

Waiting for the tunnel in CI

Start the tunnel in the background, then block until it is ready before running tests.

java -jar testingbot-tunnel.jar &
TUNNEL_PID=$!

for i in $(seq 1 60); do
  java -jar testingbot-tunnel.jar --ready 2>/dev/null && break
  sleep 2
done
java -jar testingbot-tunnel.jar --ready || { echo "tunnel not ready"; exit 1; }

npm test

kill $TUNNEL_PID

On GitHub Actions the testingbot-tunnel-action does this for you.

Docker

The testingbot/tunnel image has a built-in HEALTHCHECK that runs --ready, so docker ps shows healthy once the tunnel is actually forwarding.

docker run -d --name tb-tunnel \
  -e TESTINGBOT_KEY=... -e TESTINGBOT_SECRET=... \
  -p 127.0.0.1:4445:4445 \
  testingbot/tunnel:5.0

docker inspect --format '{{.State.Health.Status}}' tb-tunnel

With Docker Compose, other services can wait for it:

services:
  tunnel:
    image: testingbot/tunnel:5.0
    environment:
      TESTINGBOT_KEY: ${TESTINGBOT_KEY}
      TESTINGBOT_SECRET: ${TESTINGBOT_SECRET}
  tests:
    build: .
    environment:
      SELENIUM_URL: http://tunnel:4445/wd/hub
    depends_on:
      tunnel:
        condition: service_healthy
Changing the metrics port

The image's HEALTHCHECK assumes the default metrics port. If you set --metrics-port or TESTINGBOT_METRICS_PORT, override the healthcheck to match.

Kubernetes

Use /healthz as the liveness probe and /readyz as the readiness probe. Tunnel options are available as TESTINGBOT_* environment variables, so no command line is needed.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: testingbot-tunnel
spec:
  replicas: 1
  selector:
    matchLabels: { app: testingbot-tunnel }
  template:
    metadata:
      labels: { app: testingbot-tunnel }
    spec:
      containers:
        - name: tunnel
          image: testingbot/tunnel:5.0
          env:
            - name: TESTINGBOT_KEY
              valueFrom: { secretKeyRef: { name: testingbot, key: key } }
            - name: TESTINGBOT_SECRET
              valueFrom: { secretKeyRef: { name: testingbot, key: secret } }
            - name: TESTINGBOT_TUNNEL_IDENTIFIER
              value: k8s-staging
          ports:
            - containerPort: 4445   # Selenium relay
            - containerPort: 8003   # metrics and health
          startupProbe:
            httpGet: { path: /readyz, port: 8003 }
            periodSeconds: 5
            failureThreshold: 36
          readinessProbe:
            httpGet: { path: /readyz, port: 8003 }
            periodSeconds: 10
          livenessProbe:
            httpGet: { path: /healthz, port: 8003 }
            periodSeconds: 30
Who can reach port 4445

The image binds every interface so that the Service can reach it. The Selenium relay attaches your TestingBot credentials to everything it forwards, so restrict access to it with a NetworkPolicy and do not expose it outside the cluster.

Running more than one tunnel, for example one per environment? See multiple tunnels for how identifiers route tests to the right one.

--readyfile

--readyfile FILE touches a file once the tunnel is ready. It still works, but it cannot express “was ready, has since dropped”.

The file is created once and not removed while the process lives, so a tunnel that lost its SSH connection still looks ready on disk. Prefer --ready or /readyz, which ask the process for its current state.

Was this page helpful?
Last updated