Skip to main content
The Kubernetes project retired ingress-nginx on March 24, 2026. Existing installations keep running, but they no longer receive releases, bug fixes, or security patches. This guide moves an existing Ingress to the ngrok Kubernetes Operator without downtime. You run both controllers side by side, point your DNS at ngrok, and then remove ingress-nginx.
ingress-nginx (kubernetes/ingress-nginx) is a different project from F5’s NGINX Ingress Controller, which F5 still maintains. ingress-nginx annotations start with nginx.ingress.kubernetes.io/, while F5’s start with nginx.org/ or nginx.com/.
To check whether your cluster runs ingress-nginx, list its pods:

What you’ll need

  • An ngrok account on the Pay-as-you-go plan, which you need to bring your own domain.
  • A cluster where ingress-nginx serves at least one Ingress for a domain you control.
  • Access to your domain’s DNS records.
  • kubectl and Helm 3.0.0 or later.
1

Install the ngrok Kubernetes Operator

Add the ngrok Helm chart

Whenever you want to update the Operator or install a new version, you must run helm repo update to fetch the latest charts.

Get your ngrok API key and authtoken

You can get both these from the ngrok dashboard:The ngrok Kubernetes Operator provisions these as a Kubernetes secret, then uses the authtoken to create tunnels. The Operator uses your API key to manage resources via the ngrok API.

Install the Operator

Install the Operator into the default ngrok-operator namespace. You can change this in the commands below based on your cluster configuration, but by default, the Operator works with Gateway and routing resources in all namespaces.You can choose between passing your credentials directly with Helm or creating a Kubernetes Secret resource that you pass to the Helm chart. The simple method only takes one command, but with the secure method, you:
  • Prevent anyone with kubectl access to the cluster from viewing your API key and authtoken with helm get values.
  • Integrate more cleanly with infrastructure as code (IaC) tools and processes.
2

Register your domain

Choose how ngrok gets a TLS certificate for your domain during the migration.

Option 1: Upload your current certificate (no downtime)

ngrok can’t issue an automated certificate until your DNS points at ngrok. Uploading the certificate that ingress-nginx serves today lets ngrok serve valid HTTPS from the moment you change DNS.If cert-manager issues your certificate, export it from the Secret named in your Ingress’s spec.tls:
Treat tls.key as a secret, and make sure the certificate won’t expire before you finish the migration.
  1. Upload tls.crt and tls.key on the TLS Certificates page of the dashboard.
  2. Add your domain on the Domains page and select the certificate you uploaded.
  3. Don’t change your DNS records yet.
Do this before you create the ngrok Ingress. The Operator uses a domain that already exists in your account instead of reserving a new one.

Option 2: Use an automated certificate (brief certificate warning)

Skip the dashboard. When you create the ngrok Ingress in the next step, the Operator reserves your domain with an automated certificate from Let’s Encrypt.ngrok requests that certificate only after you change DNS. Until ngrok issues it, usually within a few minutes, clients see a certificate name-mismatch warning. See TLS certificate provisioning for details.
3

Create a parallel ngrok Ingress

Copy your existing Ingress to a new one with a different name and ingressClassName: ngrok. Leave the original in place so ingress-nginx keeps serving traffic until you change DNS.Drop spec.tls and any cert-manager.io/* annotations from the copy. ngrok terminates TLS with your domain’s certificate, and the Operator ignores spec.tls.
ngrok-ingress.yaml
If your original Ingress has nginx.ingress.kubernetes.io/* annotations, translate them with the annotation table before you continue.Apply the manifest and check its address:
After a few seconds, the ADDRESS column shows a hostname ending in ngrok-cname.com. This guide calls it $CNAME_TARGET. If the address stays empty, see troubleshooting.Test the ngrok path before you change DNS by sending a request straight to $CNAME_TARGET:
If you chose an automated certificate, add --insecure, because ngrok can’t issue your certificate until DNS points at it.
4

Cut over DNS

  1. A day ahead, lower the TTL on your domain’s current DNS record so the change propagates quickly.
  2. Replace the record with a CNAME that points to $CNAME_TARGET. For an apex domain such as example.com, use an ALIAS, ANAME, or flattened CNAME record instead, as described in apex domains.
  3. Confirm requests arrive in Traffic Inspector.
If you run ExternalDNS, it can publish the ngrok Ingress’s address as soon as the Operator sets it. Limit ExternalDNS to the nginx ingress class (for example, --ingress-class=nginx) until you’re ready to cut over, or let it make the DNS change for you at this step.
5

Switch to automated TLS

If you uploaded a certificate, switch your domain to automated TLS certificates on the Domains page as soon as DNS points at ngrok. ngrok keeps serving your uploaded certificate until the automated one is ready, so clients don’t notice the change.Afterward, delete the uploaded certificate from the TLS Certificates page. An uploaded certificate costs $0.27 per domain per active hour, and ngrok doesn’t renew it for you. See TLS certificates pricing.
6

Remove ingress-nginx

Once your old DNS record’s TTL has passed and no traffic reaches ingress-nginx, delete the original Ingress:
When no Ingress uses the nginx class anymore, uninstall ingress-nginx. If you installed it with Helm, run helm uninstall ingress-nginx --namespace ingress-nginx.

Map ingress-nginx annotations to ngrok

The ngrok Operator doesn’t read nginx.ingress.kubernetes.io/* annotations. Most of them map to Traffic Policy actions, which you put in an NgrokTrafficPolicy resource and attach to your Ingress with the ngrok.com/traffic-policy annotation. The table omits the nginx.ingress.kubernetes.io/ prefix. Annotations that tune NGINX itself, such as timeouts, buffer sizes, and server-snippet, have no one-to-one equivalent. Review what each one does for your app before you cut over.

Example: IP allowlist and rate limit

This ingress-nginx Ingress allows one IP range and limits each client to 60 requests per minute:
The ngrok version puts both behaviors in one NgrokTrafficPolicy and attaches it to the parallel Ingress:
ngrok-ingress.yaml
The ngrok.com/traffic-policy annotation applies to every request for the Ingress’s hostnames, and every Ingress that shares a hostname must use the same policy. If you split paths across several ingress-nginx Ingress resources to give them different annotations, add expressions such as req.url.path.startsWith('/api') to the policy’s rules instead. See using NgrokTrafficPolicy with Ingress.
Operator versions before 0.22 (Helm chart 0.24) read the older k8s.ngrok.com/ annotation prefix. Newer versions accept both prefixes but prefer ngrok.com/.

Use Gateway API instead

The ngrok Operator also implements Gateway API, the Kubernetes successor to Ingress. If you’d rather move off Ingress during this migration, ingress2gateway can convert your ingress-nginx resources with ingress2gateway print --providers=ingress-nginx. It translates some annotations, such as rewrite-target, into Gateway API filters, and warns about the ones it can’t translate. Make three changes to its output before you apply it:
  1. Set gatewayClassName: ngrok on each Gateway. ingress2gateway names the class after your old ingress class.
  2. Change each Gateway listener to protocol: HTTPS and port: 443. ingress2gateway writes HTTP listeners on port 80, and the Operator creates only an http:// endpoint for them, so HTTPS requests fail with ERR_NGROK_3200. Leave the port in each HTTPRoute’s backendRefs unchanged, because it’s your Service’s port.
  3. Replace each RegularExpression path match with a PathPrefix or Exact match. ingress2gateway writes matches such as (?i)/.* for an Ingress that uses rewrite-target or use-regex. The Operator doesn’t support regular expression matches and compares the value as a literal path prefix, so those routes return 404.
If you already applied a Gateway with an HTTP listener, delete it and apply it again. The Operator can’t change an endpoint from http:// to https:// in place, so updating the listener fails with ERR_NGROK_18005. The domain, DNS, and TLS steps in this guide stay the same. See using Gateway API for how the Operator handles these resources.