Skip to content

Migrating from NGINX Ingress

This guide covers migrating the Traceable Platform from NGINX Ingress to the Envoy-based routing mode (traceable-router). It applies to both Kurl-based (VM/bare-metal) and cloud-managed Kubernetes deployments.

Overview

The legacy NGINX Ingress mode routes external traffic directly to individual platform services using NGINX Ingress objects. Migrating to Envoy mode consolidates all routing, authentication, and header rewriting inside a single traceable-router (Envoy) Service, which can then be fronted by any ingress controller or exposed directly as a cloud load balancer.


Choosing a Migration Option

Use the matrix below to identify the right option for your environment before proceeding.

Option Deployment TPA Location When to use Guide
Option A: Migrate to Traefik VM / Kurl or cloud-managed Kubernetes Inside or outside the cluster Use Traefik as the edge router. For VM / Kurl, deploy Traefik as a DaemonSet with host ports. For cloud, expose Traefik as a LoadBalancer Service. Option A
Option B: Migrate to DaemonSet Router VM / Kurl TPA runs on a separate VM Run traceable-router as a DaemonSet and bind host ports 80 and 443 directly on every node. Option B
Option C: LB Type Router Cloud-managed Kubernetes (EKS / GKE / AKS) Inside or outside the cluster Expose traceable-router directly as a cloud LoadBalancer Service. Use this when you do not need a separate ingress controller. Option C

TPA = Traceable Platform Agent.


Prerequisites

  • Traceable Installer (TI) version: 0.1.344 or later
  • Platform version: 1.40.x or later
  • kubectl access to the cluster with sufficient permissions
  • Access to the Traceable Installer UI with admin credentials

Firewall: Docker Hub domains

If your nodes pull images from Docker Hub, allowlist the following domains:

Domain Purpose
*.docker.io Docker Hub image registry (includes registry-1.docker.io)
production.cloudflare.docker.com CDN-backed image layer storage

Reference: Docker Desktop Allowlist


If You Are Running on an Older Version (below 1.40.x)

If your platform is currently on a version older than 1.40.x, you must first upgrade to 1.40.2 before performing the NGINX migration. Follow these steps:

Step 1 — Download the 1.40.2 Workflow File

Follow the Download Workflow File guide to pull the 1.40.2 workflow bundle to your local machine.

Step 2 — Upload the Workflow File via TI

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Upgrade Cluster.
  3. Click Upload Cluster Version and upload the workflow-1.40.2.tgz file.
  4. Select the uploaded version in the dropdown and click Next.
  5. Review the changes and click Apply.

Step 3 — Complete the Migration

Once the workflow file is applied and you are on the Install Workflow page, proceed with the migration steps for your deployment type:

Step 4 — Upgrade the Platform

After the NGINX migration is complete, follow the Upgrading the Platform guide to complete the 1.40.2 upgrade rollout.


Migration Options

Choose one of the options below based on your deployment type and desired edge routing model.

⚠️ Downtime notice (VM deployments) On Kurl-based clusters, NGINX binds host ports 80 and 443 directly on every node. Any new component that also requires those host ports — Traefik (Option A) or traceable-router in DaemonSet mode (Option B) — will be deployed successfully but its pods will remain in Pending state until NGINX is removed and the ports are freed. Plan for approximately 5 minutes of downtime between removing NGINX and the new pods becoming Running.


Option A: Migrate to Traefik

Use this option when you want Traefik to remain the external entry point and route traffic to traceable-router through Traefik IngressRoute CRDs.

  • VM / Kurl deployments: Deploy Traefik as a DaemonSet with hostPort bindings on ports 80 and 443.
  • Cloud deployments: Deploy Traefik with a LoadBalancer Service so the cloud provider provisions an external load balancer for Traefik.

Step 1 — Install Traefik

VM / Kurl: Use Traefik with host ports

Deploy Traefik as a DaemonSet so it binds host ports 80 and 443, and expose it via a ClusterIP Service.

Note: The Helm install will succeed, but Traefik pods will remain in Pending state because NGINX is currently holding ports 80 and 443. The pods will start automatically once NGINX is scaled down in Step 4.

Apply the following Helm values override when installing the Traefik chart:

deployment:
  enabled: true
  kind: DaemonSet

ports:
  web:
    hostPort: 80
  websecure:
    hostPort: 443

service:
  enabled: true
  spec:
    type: ClusterIP

ingressClass:
  name: traefik

Install Traefik:

helm repo add traefik https://traefik.github.io/charts
helm repo update

helm install traefik traefik/traefik \
  --namespace traefik --create-namespace \
  -f traefik-values.yaml

Confirm the Helm install succeeded and the ingress class is registered (pods will be Pending at this point — this is expected):

kubectl get pods -n traefik           # STATUS: Pending (port conflict with NGINX)
kubectl get ingressclass traefik

Cloud: Use Traefik as a LoadBalancer

For cloud-managed Kubernetes clusters, expose Traefik directly with a cloud LoadBalancer Service. The cloud provider provisions the external load balancer, and Traefik forwards traffic to traceable-router using the IngressRoute CRDs rendered by traceable-ingress.

Apply the following Helm values override when installing the Traefik chart:

deployment:
  enabled: true
  kind: Deployment

service:
  enabled: true
  spec:
    type: LoadBalancer

ports:
  web:
    port: 80
  websecure:
    port: 443

ingressClass:
  name: traefik

Install Traefik:

helm repo add traefik https://traefik.github.io/charts
helm repo update

helm install traefik traefik/traefik \
  --namespace traefik --create-namespace \
  -f traefik-values.yaml

Wait for the external IP/hostname to be assigned:

kubectl get svc -n traefik traefik --watch

Retrieve the Traefik load balancer address:

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

Update your DNS record for traceable.yourcompany.com to point to the Traefik load balancer before validation.

Step 2 — Install traceable-router

Deploy the traceable-router Helm chart via the Traceable Installer UI.

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Install Workflow.
  3. Click on the traceable-router
  4. Click Helm Upgrade to apply the changes.

Confirm the pod is ready:

kubectl get pods -n traceable -l app.kubernetes.io/name=traceable-router

Step 3 — Switch traceable-ingress to Envoy + Traefik

Update the traceable-ingress chart to render Traefik IngressRoute CRDs that point to traceable-router instead of the legacy per-service NGINX Ingress objects.

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Install Workflow.
  3. Click on the traceable-ingress
  4. Open the user values (user-values.yaml) for the traceable-ingress chart and add the following:
router: envoy
controller: traefik-ingress
  1. Click Helm Upgrade to apply the changes.

Step 4 — Cut Over Traffic and Validate

For VM / Kurl deployments, scale down NGINX to release host ports 80 and 443 so Traefik pods can start. For cloud deployments, update DNS to point to the Traefik load balancer address. This lets you validate the new routing path before committing to uninstallation.

⏱️ Downtime starts here (~5 minutes). For VM / Kurl deployments, scaling down NGINX frees host ports 80 and 443. Traefik pods will transition from Pending to Running within a few seconds, after which traffic resumes. For cloud deployments, downtime depends on DNS TTL propagation.

For VM / Kurl deployments, if NGINX is running as a DaemonSet, scale it down by applying a non-matching nodeSelector:

kubectl -n <nginx-namespace> patch daemonset <nginx-daemonset-name> \
  -p '{"spec": {"template": {"spec": {"nodeSelector": {"non-existing": "true"}}}}}'

For VM / Kurl deployments, watch Traefik pods come up once the ports are freed:

kubectl get pods -n traefik --watch

Step 5 — Validate

Verify the new routing path is fully functional:

# Confirm IngressRoutes were created
kubectl get ingressroute -n traceable

# End-to-end check — login page should return 200 or 302
curl -kI https://traceable.yourcompany.com

# Check traceable-router logs for traffic
kubectl logs -n traceable -l app.kubernetes.io/name=traceable-router --tail=50

Step 6 — Uninstall NGINX

Once validated, permanently remove NGINX. See Uninstalling NGINX below.

If you need to roll back before uninstalling, see Rolling Back below.


Option B: Migrate to DaemonSet Router

In this approach, traceable-router runs as a Kubernetes DaemonSet and binds host ports 80 and 443 directly on every node. This eliminates the need for a separate ingress controller — the Envoy proxy itself acts as the edge.

Step 1 — Upgrade traceable-router to DaemonSet mode

Note: The Helm upgrade will succeed, but traceable-router pods will remain in Pending state because NGINX is currently holding ports 80 and 443. The pods will start automatically once NGINX is scaled down in Step 3.

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Install Workflow.
  3. Click on the traceable-router
  4. Open the user values (user-values.yaml) for the traceable-router chart and add the following:
mode: DaemonSet
containerPorts:
  https: 443
  http: 80
  1. Click Helm Upgrade to apply the changes.

Confirm the Helm upgrade succeeded and the DaemonSet exists (pods will be Pending at this point — this is expected):

kubectl get daemonset -n traceable traceable-router
kubectl get pods -n traceable -l app.kubernetes.io/name=traceable-router -o wide  # STATUS: Pending

Step 2 — Switch traceable-ingress to Envoy mode

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Install Workflow.
  3. Click on the traceable-ingress
  4. Open the user values (user-values.yaml) for the traceable-ingress chart and add the following:
router: envoy
controller: none
  1. Click Helm Upgrade to apply the changes.

Step 3 — Scale Down NGINX and Validate

Before fully removing NGINX, scale it down to release host ports 80 and 443 so traceable-router pods can start. This lets you validate the new routing path before committing to uninstallation.

⏱️ Downtime starts here (~5 minutes). Scaling down NGINX frees host ports 80 and 443. traceable-router pods will transition from Pending to Running within a few seconds, after which traffic resumes.

If NGINX is running as a DaemonSet, scale it down by applying a non-matching nodeSelector:

kubectl -n <nginx-namespace> patch daemonset <nginx-daemonset-name> \
  -p '{"spec": {"template": {"spec": {"nodeSelector": {"non-existing": "true"}}}}}'

Watch traceable-router pods come up once the ports are freed:

kubectl get pods -n traceable -l app.kubernetes.io/name=traceable-router --watch

Step 4 — Validate

Verify the new routing path is fully functional:

# End-to-end check — login page should return 200 or 302
curl -kI https://traceable.yourcompany.com/

# Check traceable-router logs for traffic
kubectl logs -n traceable -l app.kubernetes.io/name=traceable-router --tail=50

Step 5 — Uninstall NGINX

Once validated, permanently remove NGINX. See Uninstalling NGINX below.

If you need to roll back before uninstalling, see Rolling Back below.


Option C: LB Type Router

Use this option if you are running Traceable on a managed Kubernetes cluster and want to expose traceable-router directly through a cloud LoadBalancer Service.

⚠️ Downtime notice (cloud deployments) Unlike VM deployments, NGINX on cloud clusters does not bind host ports directly on nodes, so traceable-router can be provisioned as a LoadBalancer Service independently — pods will not enter a Pending state due to port conflicts. However, approximately 5 minutes of downtime occurs at the DNS cutover: until the DNS TTL expires and clients refresh, agents and users will continue to send traffic to the old NGINX load balancer. To minimise this window, lower the TTL of your DNS record to 60 seconds at least one full TTL cycle before starting the migration (e.g. if your current TTL is 3600 seconds, lower it 1 hour in advance) and restore it after.

Step 1 — Deploy traceable-router with a LoadBalancer Service

Switch traceable-ingress to Envoy mode with controller: none and expose traceable-router directly via a cloud LoadBalancer Service. No ingress controller is needed.

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Install Workflow.
  3. Click on the traceable-router
  4. Open the user values (user-values.yaml) and update both charts as follows:
# traceable-router values
httpEnabled: false
httpsEnabled: true
service:
  type: LoadBalancer
  primaryPortDef:
    name: https
    port: 443
    targetPort: https
  secondaryPortDef:
    port: 0
  1. Click Helm Upgrade to apply the changes.
  2. Click on the traceable-ingress
  3. Open the user values (user-values.yaml) and update both charts as follows:
# traceable-ingress values
router: envoy
controller: none
  1. Click Helm Upgrade to apply the changes.

For TLS passthrough or ACM-terminated TLS on AWS, see the AWS EKS scenarios in the Platform Connectivity guide.

Wait for the external IP/hostname to be assigned:

kubectl get svc traceable-router -n traceable --watch

Step 2 — Point DNS to the New Load Balancer

Retrieve the new load balancer address:

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

Update your DNS record for traceable.yourcompany.com:

  • Create a CNAME record if the output is a hostname (typical on EKS with NLB).
  • Create an A record if the output is an IP address (typical on GKE/AKS).

Allow DNS TTL to propagate before proceeding.

Step 3 — Validate

# Service should show an external address
kubectl get svc traceable-router -n traceable
# End-to-end
curl -kI https://traceable.yourcompany.com/

Step 4 — Uninstall NGINX Ingress

Once validated, permanently remove NGINX. See Uninstalling NGINX below.

If you need to roll back before uninstalling, see Rolling Back below.


Uninstalling NGINX

Self-managed NGINX (installed independently)

If you installed NGINX yourself (outside of the Traceable installer), uninstall it using whichever method you used to deploy it.

Helm-managed:

# Find the release name and namespace
helm list -A | grep nginx

helm uninstall <release-name> --namespace <namespace>

YAML-managed:

# Delete all nginx-ingress resources applied from a manifest
kubectl delete -f nginx-ingress-deploy.yaml

# Or delete by label selector if the manifest is unavailable
kubectl delete all,clusterrole,clusterrolebinding,ingressclass \
  -l app.kubernetes.io/name=ingress-nginx --all-namespaces

Verify removal:

kubectl get pods -A | grep nginx
kubectl get ingressclass | grep nginx

NGINX deployed via Traceable Installer

If NGINX was installed and managed by the Traceable installer (i.e. the traceable-nginx chart is present in your cluster), disable it through the installer rather than deleting it manually.

  1. Log in to the Traceable Installer (TI) using your admin credentials.
  2. Navigate to Upgrade Cluster.
  3. Click on Upgrade Operator.
  4. Add the following in the helm override section:
traceable-nginx:
  enabled: false
  1. Click Trigger Upgrade to apply the changes.

Verify the NGINX pods are terminated:

kubectl get pods -n traceable | grep nginx

Rolling Back

If you need to revert to NGINX before permanently uninstalling it, restore the NGINX DaemonSet by removing the temporary nodeSelector patch:

kubectl -n <nginx-namespace> patch daemonset <nginx-daemonset-name> \
  --type json \
  -p='[{"op": "remove", "path": "/spec/template/spec/nodeSelector/non-existing"}]'

This scales NGINX back up. Once NGINX pods are Running again, revert the traceable-ingress and traceable-router Helm values through the Traceable Installer.


Post-Migration Checklist

  • traceable-router pod is Running
  • DNS resolves to the new load balancer / Traefik host port
  • https://traceable.yourcompany.com/ returns HTTP 200 or 302
  • NGINX pods and ingress resources are gone (kubectl get pods,ingress -A | grep nginx)
  • Agent connectivity is confirmed (agents send traces and receive config)
  • Installer UI (TI) WebSocket (/ti/ws) is functional

Troubleshooting

502 / 503 after switching to Envoy mode

Envoy's HTTP listener speaks h2c (HTTP/2 cleartext) on port 8000. Ensure the ingress controller targets port 8000 with HTTP/2 enabled. For Traefik this is handled automatically by the rendered IngressRoute; for other controllers verify backend-protocol: h2c or equivalent.

Login redirects to wrong hostname or loops

Ensure passHostHeader: true is set so the original Host header reaches Envoy. Envoy uses the :authority header to build login redirect URLs.

WebSocket (/ti/ws) drops

The /ti/ws path must use HTTP/1.1. The Traefik templates in traceable-ingress render a dedicated route for this automatically. For custom setups, ensure Upgrade headers are forwarded and the path is not forced to HTTP/2.

DNS not resolving after update

Check TTL expiry (dig traceable.yourcompany.com +ttl) and confirm the old NGINX-facing CNAME or A record has been replaced. During transition, both paths can coexist until the TTL drains.

Traceable Installer UI is inaccessible

If the TI UI is unreachable (for example, during the migration window while NGINX is down), you can access it directly via kubectl port-forward:

  1. Port-forward the traceable-installer-0 pod to your local machine:
kubectl port-forward -n traceable traceable-installer-0 8000:8000 --address 0.0.0.0
  1. Open a browser and navigate to http://<node-ip>:8000 (or http://localhost:8000 if running locally).

  2. Log in using your TI admin credentials.

Note: Ensure port 8000 is allowed through your firewall/security group on the machine where you are running the port-forward command.


Support

For migration assistance, contact support@harness.io.

When requesting support, provide:

  • Your deployment type (Kurl/VM or cloud-managed Kubernetes)
  • The traceable-ingress and traceable-router values currently in use
  • Output of kubectl get svc,ingress,ingressroute,pods -n traceable
  • Relevant traceable-router and ingress controller pod logs