Skip to content

Connecting to the Traceable Platform

This guide explains the different ways to expose the Traceable Platform so that users, agents, and API clients can reach it. It covers the two routing modes shipped with the platform, the central traceable-router (Envoy) component, and detailed configuration for cloud load balancers, NGINX, Traefik, OpenShift Routes.

Overview

All external traffic to the platform — the UI, the GraphQL/REST APIs, the IAM/login flows, the OpenTelemetry trace ingestion (gRPC), and the agent control-plane — enters through a single logical edge. How that edge is exposed depends on your Kubernetes distribution and the ingress technology available in your environment.

The platform supports two routing modes, selected through the router value in the traceable-ingress Helm chart:

Mode router value Edge component Best for
Legacy legacy Multiple NGINX Ingress objects routing directly to backend services Clusters where ingress-nginx is installed and cluster-scoped permissions are available
Envoy envoy A single traceable-router (Envoy) Service that you expose via any controller or load balancer Cloud Kubernetes, OpenShift, Traefik, and any cluster where ingress-nginx cannot be installed

Rule of thumb: If you can install and run ingress-nginx with cluster scope, legacy mode works out of the box. In every other case — managed cloud clusters, OpenShift, namespace-scoped installs, or environments standardized on a non-NGINX ingress — use Envoy mode and pick one of the connectivity options below.


Architecture

Legacy mode (NGINX Ingress)

In legacy mode, the traceable-ingress chart renders a set of NGINX Ingress resources (ingress-http-auth, ingress-grpc-auth, ingress-http-noauth, and so on). Each one routes specific paths directly to the corresponding platform service (ui, graphql, iam/iam-v2, traceable-config-service, hypertrace-collector, scan-manager, etc.).

Authentication is delegated to NGINX's external-auth feature: protected ingresses carry nginx.ingress.kubernetes.io/auth-url and auth-signin annotations that call the jwt-auth service before forwarding the request.

Client ─► Cloud/Edge LB ─► ingress-nginx ─► [ui | graphql | iam | collector | ...]
                                  └─ auth-url ─► jwt-auth

Requirements: a working ingress-nginx controller, cluster-scoped RBAC, and the NGINX external-auth annotations support.

Envoy mode (traceable-router)

In Envoy mode, all of that path routing, external authorization, login redirects, gRPC-web translation, and header rewriting is performed inside the traceable-router Envoy proxy. The ingress layer no longer needs to understand the platform's routing table — it only has to deliver traffic to one Service.

Client ─► [ Cloud LB | NGINX | Traefik | OpenShift Route ] ─► traceable-router (Envoy) ─► all platform services

This indirection is what makes Envoy mode portable: any controller that can forward HTTP/2 (h2c) or HTTPS to a single backend Service can front the platform. The traceable-ingress chart provides ready-made templates for several controllers, selected with the controller value:

router: envoy
controller: none   # one of: ingress-nginx | nginx | traefik-ingress | openshift-route | none

Use controller: none when you expose the traceable-router Service directly (for example, with a cloud LoadBalancer Service) and do not want the chart to render any ingress object.

Applying the values in this guide: The traceable-router and traceable-ingress snippets below are chart values. At install time you supply them through the installer's per-chart override mechanism — cluster.helmValues.traceable-router and cluster.helmValues.traceable-ingress. See Overriding Helm Chart Values for how to author and pass the override file.


The traceable-router component

The traceable-router chart (chart name: traceable-router) deploys a single Envoy proxy. Understanding its listeners and Service is key to configuring any of the Envoy-mode options.

Listeners and ports

Listener Container port Protocol Toggle Notes
HTTP 8000 HTTP/2 cleartext (h2c) httpEnabled Use when TLS is terminated upstream (at the LB/ingress).
HTTPS 8443 HTTPS (TLS terminated in Envoy) httpsEnabled Uses the certificate from tlsSecretName.
Admin 9901 HTTP adminEnabled Stats (/stats/prometheus) and health (/ready).

Service

By default the chart creates a ClusterIP Service:

service:
  type: ClusterIP
  primaryPortDef:
    name: http
    port: 8000
    targetPort: http      # -> container port 8000 (h2c)
  secondaryPortDef:
    name: https
    port: 443
    targetPort: https     # -> container port 8443 (TLS)

To expose the platform you either:

  • change service.type to LoadBalancer (cloud direct exposure), or
  • keep it as ClusterIP and place an ingress controller (NGINX, Traefik, OpenShift Route) in front of it.

TLS strategy primer

There are two independent decisions:

  1. Where TLS is terminated — at the cloud load balancer / ingress, or inside traceable-router.
  2. Whether the backend hop is encrypted — i.e. does the controller talk to traceable-router over h2c (8000) or HTTPS (443/8443).

These two choices produce the configurations described per-option below.


Option 1: Cloud Kubernetes Service type LoadBalancer

On managed Kubernetes (EKS, GKE, AKS) the simplest and most common approach is to expose traceable-router directly with a LoadBalancer Service. The cloud provider provisions an external load balancer, and you do not deploy any ingress controller.

Set controller: none in traceable-ingress (so no ingress object is rendered) and configure the traceable-router Service.

Generic cloud LoadBalancer (TLS terminated in Envoy)

The load balancer passes TLS straight through to Envoy, which terminates it using tlsSecretName:

# traceable-router values
httpsEnabled: true
httpEnabled: false
tlsSecretName: traceable-router-certs
service:
  type: LoadBalancer
  primaryPortDef:
    name: https
    port: 443
    targetPort: https     # -> Envoy 8443
  secondaryPortDef:
    port: 0               # disables the second port
LB:443 ──[TCP/TLS passthrough]──► traceable-router:443 (Envoy terminates TLS)

AWS EKS scenarios

AWS supports several patterns depending on where you want TLS terminated and whether you use an ACM certificate on the Network Load Balancer (NLB). Apply these annotations on the traceable-router Service.

1. TLS passthrough at the NLB

TLS is terminated by Envoy; the NLB forwards TCP only.

NLB:443 [tls-passthrough] ──► traceable-router:8443 (https)

httpsEnabled: true
httpEnabled: false
service:
  type: LoadBalancer
  primaryPortDef:
    name: https
    port: 443
    targetPort: https
  secondaryPortDef:
    port: 0
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-type: external
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip

2. TLS termination at the NLB (ACM certificate)

The NLB terminates TLS with an ACM certificate and forwards cleartext HTTP/2 to Envoy.

NLB:443 [ACM cert] ──► traceable-router:8000 (http/h2c)

httpEnabled: true
httpsEnabled: false
service:
  type: LoadBalancer
  primaryPortDef:
    name: https
    port: 443
    targetPort: http
  secondaryPortDef:
    port: 0
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-west-2:ACCT_ID:certificate/CERT_UUID"
    service.beta.kubernetes.io/aws-load-balancer-type: external
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip

3. TLS termination at the NLB and re-encryption to Envoy

The NLB terminates the client TLS (ACM), then opens a new TLS connection to Envoy (defense in depth).

NLB:443 [ACM cert] ──[ssl]──► traceable-router:8443 (https)

httpsEnabled: true
httpEnabled: false
service:
  type: LoadBalancer
  primaryPortDef:
    name: https
    port: 443
    targetPort: https
  secondaryPortDef:
    port: 0
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: ssl
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-west-2:ACCT_ID:certificate/CERT_UUID"
    service.beta.kubernetes.io/aws-load-balancer-type: external
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip

4. TLS passthrough with a Classic Load Balancer

CLB:443 [tls-passthrough] ──► traceable-router:8443 (https)

httpsEnabled: true
httpEnabled: false
service:
  type: LoadBalancer
  primaryPortDef:
    name: https
    port: 443
    targetPort: https
  secondaryPortDef:
    port: 0

GKE / AKS notes

  • GKE: A Service of type LoadBalancer provisions a regional TCP/UDP load balancer. For TLS passthrough, follow the generic example above (TLS terminated in Envoy). To terminate TLS at a Google load balancer instead, use a GKE Ingress/Gateway with a managed certificate pointing at the traceable-router Service.
  • AKS: A Service of type LoadBalancer provisions an Azure Load Balancer. Use the generic passthrough example, or add service.beta.kubernetes.io/azure-load-balancer-internal: "true" for an internal-only endpoint.

DNS

After the load balancer is provisioned, create a DNS record for your platform host pointing at the load balancer's external address:

kubectl get svc traceable-router -n traceable \
  -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{.status.loadBalancer.ingress[0].ip}'

Create a CNAME (for a hostname) or an A record (for an IP) for traceable.yourcompany.com.


Option 2: NGINX Ingress

If your cluster runs the ingress-nginx controller, you can use legacy mode, where NGINX routes directly to the platform services.

# traceable-ingress values
router: legacy
host: traceable.yourcompany.com
tlsSecretName: star-yourcompany-com
ingressClassName: nginx
isIamV2Enabled: false        # set true if IAM v2 is enabled
ingress:
  authUrl: http://jwt-auth.traceable.svc.cluster.local
  authSignin: https://traceable.yourcompany.com/login
  proxyBodySize: "10m"
  # configurationSnippet sets the in-cluster DNS resolver used by NGINX
  configurationSnippet: |-
    resolver 10.0.0.10 ipv6=off;

Notes:

  • Set configurationSnippet's resolver to your cluster's DNS service IP (kubectl get svc -n kube-system kube-dns).
  • TLS is terminated by NGINX using the tlsSecretName secret. Front NGINX with whatever external load balancer your environment provides.
  • gRPC ingestion paths (OpenTelemetry, config service) are served via dedicated Ingress objects annotated with backend-protocol: GRPC.

Tip: You can also run NGINX in front of the Envoy router by setting router: envoy and controller: nginx. In that mode NGINX forwards everything to the single traceable-router Service instead of routing per-path. Use this when you want NGINX as the edge but prefer Envoy to own the platform routing table.


Option 3: Traefik (Envoy mode)

For clusters standardized on Traefik, use Envoy mode with the Traefik controller. The chart renders Traefik IngressRoute (and, when the backend hop is encrypted, ServersTransport) CRDs that point at traceable-router.

TLS terminated at Traefik, h2c to Envoy

This is the common case: Traefik terminates client TLS and forwards cleartext HTTP/2 to Envoy on port 8000.

router: envoy
controller: traefik-ingress
host: traceable.yourcompany.com
tlsSecretName: star-yourcompany-com
traceableRouter:
  serviceName: traceable-router
  servicePort: 8000
  tlsEnabled: false           # h2c backend hop
traefikIngress:
  ingressClassName: traefik
  entryPoints:
    - websecure
  passHostHeader: true

The chart additionally renders a dedicated route for /ti/ws (the installer WebSocket path) so it traverses over HTTP/1.1 cleanly.

Re-encrypt: TLS to Envoy as well

To encrypt the Traefik → Envoy hop, set traceableRouter.tlsEnabled: true. The chart then targets port 443 with scheme: https and generates a ServersTransport for backend verification.

router: envoy
controller: traefik-ingress
host: traceable.yourcompany.com
tlsSecretName: star-yourcompany-com
traceableRouter:
  serviceName: traceable-router
  servicePort: 443            # traceable-router Service must expose 443 (httpsEnabled)
  tlsEnabled: true
traefikIngress:
  ingressClassName: traefik
  entryPoints:
    - websecure
  passHostHeader: true
  serversTransport:
    serverName: ""            # defaults to .host
    insecureSkipVerify: false
    rootCAsSecrets: []        # Secrets holding CA certs (tls.ca / ca.crt) to trust

When tlsEnabled: true, ensure the traceable-router Service exposes port 443 (httpsEnabled: true). If you use a self-signed backend certificate, either provide the CA via serversTransport.rootCAsSecrets / selfSignedCertificates, or set insecureSkipVerify: true (not recommended for production).


Option 4: OpenShift Route (Envoy mode)

On OpenShift, ingress-nginx typically cannot be used. Use Envoy mode with the OpenShift Route controller; the chart renders a route.openshift.io/v1 Route pointing at traceable-router.

router: envoy
controller: openshift-route
host: traceable.yourcompany.com
traceableRouter:
  serviceName: traceable-router
openshiftRoute:
  weight: 100
  tlsTermination: passthrough   # passthrough | terminate | reencrypt

TLS termination modes

tlsTermination Behavior Requirements
passthrough The Route forwards encrypted traffic to Envoy, which terminates TLS. traceable-router must serve HTTPS (httpsEnabled: true).
terminate The OpenShift router terminates TLS (edge) and forwards cleartext to Envoy. insecureEdgeTerminationPolicy: Redirect is set. Provide edge certs via openshiftRoute.certificates, or rely on the default router cert.
reencrypt The router terminates client TLS, then re-encrypts to Envoy. traceableRouter.tlsEnabled: true is required (the chart fails the render otherwise).

For terminate and reencrypt, supply certificates in PEM format:

openshiftRoute:
  tlsTermination: reencrypt
  certificates:
    key: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
    certificate: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
    caCertificate: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
    destinationCACertificate: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
traceableRouter:
  tlsEnabled: true

DNS for OpenShift Routes

Create a CNAME for your platform host pointing at the Route's canonical hostname:

oc get route -n traceable \
  -o jsonpath='{.items[0].status.ingress[0].routerCanonicalHostname}'

See the OpenShift Configuration guide for the full namespace-scoped install workflow.


Choosing the right option

Your environment Recommended option
EKS / GKE / AKS (managed cloud) Cloud LoadBalancer (direct traceable-router Service)
ingress-nginx already installed, cluster scope NGINX Ingress (legacy)
Red Hat OpenShift OpenShift Route
Traefik-standardized cluster Traefik
No ingress controller / want minimal moving parts Cloud LoadBalancer, or Envoy mode with controller: none behind your own L4 LB

Validation

After exposing the platform, validate connectivity:

# Service and (if LoadBalancer) external address
kubectl get svc traceable-router -n traceable

# Envoy readiness via the admin port
kubectl port-forward -n traceable svc/traceable-router-internal 9901:9901
curl -s http://localhost:9901/ready

# End-to-end: the login page should respond
curl -kI https://traceable.yourcompany.com/login

Expected: the /login request returns an HTTP 200 (or a 302 redirect to the login flow), confirming traffic reaches Envoy and the IAM service.


Troubleshooting

TLS handshake failures / certificate errors

Symptoms: Browser shows certificate warnings, or curl reports SSL certificate problem.

Causes & solutions:

  • The certificate in tlsSecretName does not match host. Verify the SAN covers your DNS name.
  • For re-encrypt setups with a self-signed backend cert, the controller cannot verify Envoy's certificate. Provide the CA (serversTransport.rootCAsSecrets for Traefik, destinationCACertificate for OpenShift), or temporarily set insecureSkipVerify: true to isolate the issue.

502 / 503 from the ingress

Symptoms: The edge responds but returns 502 Bad Gateway or 503.

Causes & solutions:

  • Backend protocol mismatch. Envoy's HTTP listener is h2c (HTTP/2 cleartext). Ensure the controller forwards HTTP/2 (scheme: h2c for Traefik, backend-protocol: GRPC/HTTP2 for NGINX) when targeting port 8000.
  • traceable-router pods are not ready. Check kubectl get pods -n traceable -l app.kubernetes.io/name=traceable-router and the /ready admin endpoint.

Login redirect loops or wrong host

Symptoms: Repeated redirects to /login, or redirects to the wrong hostname.

Causes & solutions:

  • The original Host header is not preserved. Enable host pass-through (passHostHeader: true for Traefik; preserve Host on NGINX). Envoy builds the login redirect from the inbound :authority.

WebSocket (/ti/ws) or installer UI fails

Symptoms: The installer (TI) WebSocket connection drops.

Causes & solutions:

  • The /ti/ws path must traverse over HTTP/1.1, not HTTP/2. The Traefik templates render a dedicated /ti/ws route for this; ensure you did not override it. For custom edges, route /ti/ws over HTTP/1.1 with Upgrade headers allowed.

LoadBalancer stuck in <pending>

Symptoms: kubectl get svc traceable-router shows EXTERNAL-IP: <pending>.

Causes & solutions:

  • The cloud controller cannot provision the LB. On EKS, confirm the AWS Load Balancer Controller is installed and the subnet tags are correct. Check kubectl describe svc traceable-router -n traceable events.

Support

For connectivity and ingress assistance, contact support@harness.io.

When requesting support, provide:

  • The routing mode (legacy or envoy) and controller value
  • The traceable-router Service definition and annotations
  • Output of kubectl get svc,ingress,route -n traceable
  • Relevant controller and traceable-router pod logs