• Updated
Migrate from ingress-nginx to the ngrok Operator without downtime
Updated: November 2026
In November 2025, the Kubernetes SIG Network and the Security Response Committee (SRC) announced the retirement of ingress-nginx. It received best-effort support until March 2026, at which point all maintenance stopped. The repository is now read-only and that’s that.
We provide our own Kubernetes Operator at ngrok and have written in the past about what differentiates the ngrok Operator from others. In this post I’ll show you how to migrate from ingress-nginx to the ngrok operator.
Check which NGINX controller you run
Two different projects run NGINX as a Kubernetes Ingress controller, and only
one of them is retired. ingress-nginx (kubernetes/ingress-nginx) is the
community controller that Kubernetes retired. The NGINX Ingress Controller
(nginx/kubernetes-ingress) is a separate product that F5 still
maintains.
Check the controller behind your ingress classes:
If the CONTROLLER column says k8s.io/ingress-nginx, you run the retired
controller and the rest of this post is for you. If it says
nginx.org/ingress-controller, you run F5’s controller, which still gets
security fixes. The migration steps below still work if you want to move to
ngrok, but you aren’t racing an end-of-life date.
Prerequisites
I’m going to assume you have a cluster with ingress-nginx running in it. For
example, here it is in the cluster I’m using for this post.
And to demonstrate that things are working correctly, I’ve created a small
deployment using the testcontainers/helloworld image:
If I request http://test.samwho.dev/ping using httpie, I see a PONG
response:
Sign up for ngrok
To use the ngrok Operator, sign up for an account at ngrok.com. Once you have an account, make note of your authtoken and you need to create an API key. You’ll find your authtoken in the Your Authtoken part of the dashboard. For your API key, go to the API Keys part of the dashboard and click + New API Key then follow the steps.
"Do I need to pay to use the ngrok operator?"
Install the ngrok Operator
The recommended way to install the ngrok Operator is through Helm.
Substitute <YOUR_API_KEY> and <YOUR_AUTH_TOKEN> with your API key and
authtoken from the previous step.
Values you pass with --set stay in the Helm release, so anyone who can run
helm get values on it can read your API key and authtoken. To keep them out of
Helm, put them in a Kubernetes Secret first and give the chart the Secret’s
name instead. read -s prompts for each value, so neither one lands in your
shell history:
If all goes well you should see a message that ends like this:
And the pods should settle into the Running status:
"Sam, why is your operator-manager pod newer than your operator-agent pod?"
helm install command by hand while writing this post (I don’t know what I was thinking)
and I tried to --set credentials.authToken=$NGROK_AUTHTOKEN. I didn’t notice that it should actually be
credentials.authtoken with a lowercase t. Took me a little while to figure out what I did wrong, and fixing it
recreated the pod. Hopefully you don’t trip over the same thing!Register a domain in ngrok
Before you create an ngrok Ingress resource, you need to register the domain you want to use in ngrok. Head over to the Domains section of the dashboard and register the domain you want to use by clicking the +New Domain button.
Note: You must already own the domain you want to use. ngrok is not a domain registrar, the registration process in ngrok is so that we know how to route traffic to you, it is not the same as buying a domain with a registrar.
test.samwho.dev) as well as ngrok domains (e.g.
test.ngrok.io). This migration guide covers the process of migrating a non-ngrok domain into ngrok.Without a certificate (involves downtime)
The easiest way to migrate is to register a domain and get ngrok to automatically provision a certificate for you. However, doing this will result in a short period of downtime. In my testing, this lasted about 5 minutes. This is because ngrok uses Let’s Encrypt to provision certificates, and it needs your DNS name to point at ngrok to pass the ACME HTTP01 challenge. While the certificate is being provisioned, your domain will be considered insecure and most clients/browsers will refuse to connect to it.
The next section will cover how to avoid this downtime, but the remainder of this section will focus on creating a domain with an ngrok-managed certificate.
Fill in the domain creation form with your desired domain name, and leave everything else default. Here’s what it looked like for me:
When you’ve created the domain, you’ll be prompted to set the domain’s DNS record to be a CNAME pointing at ngrok. I recommend holding off from doing this right now, because when you do it you’ll break the domain for users until you’ve created your new Ingress resource. You’ll make the DNS switch as the last step of this migration process.
With a certificate (no downtime)
To avoid any downtime, you need to create a domain in ngrok with a certificate. After migrating, you’ll be able to switch to a certificate managed by ngrok without incurring any downtime. This step is just required to avoid downtime during the migration.
"Does uploading my own certificate cost extra?"
To demonstrate, I’ve configured my cluster to use cert-manager and I’ve
issued a certificate for test.samwho.dev with Let’s Encrypt. My
helloworld Ingress resource now looks like this:
I can verify it’s set up correctly by looking at the TLS section of the Ingress resource:
ngrok asks for 2 pieces of information when setting up a domain with your own
certificate: the certificate itself, and the certificate private key. I can grab
both of these from the secret that cert-manager created for my Ingress, which
is called letsencrypt-prod as seen above. You should treat these as
sensitive information and handle them securely.
If you’re using a different certificate manager, the way that you get this information may vary. You will know if you have the right values because the certificate looks like this:
And the private key looks like this:
With these in hand, navigate to the TLS certificates section of the ngrok dashboard and click +Create TLS Certificate. You’ll see a dialog that looks like this:
Put your certificate and private key in the appropriate boxes. In the
description field, I recommend putting the domain name that the certificate
applies to. In my case test.samwho.dev. Click save when you’re done.
Now to register the domain. Head over to the Domains section of the ngrok dashboard and click +Create Domain. Click on the certificate dropdown and select “Upload Certificate”. You should see your certificate:
Click save, and you’ll be shown instructions for setting up the CNAME record for your domain. Don’t do this just yet. You need to set up the ngrok Ingress resource first, which we’ll do next.
Create an ngrok Ingress
Rather than updating the existing Ingress resource, you’ll create a new one next to it that uses the ngrok ingress class. Here’s the Ingress resource I used in my cluster:
Save this as ngrok-ingress.yaml and apply it:
When the ngrok Operator picks up this Ingress, it will assign it an
ngrok-cname.com address like so:
That 2awrvpxvfbuzgneam.5nqklokassiaw4xte.ngrok-cname.com address is my
ngrok-cname.com address, yours will be a different, unique value. You’ll use
it later to update your DNS record.
Troubleshooting
If you don’t see an ngrok-cname.com address, for example your “Address” field
is empty, there are a few debugging steps you can follow to figure out the
problem.
Your first port of call should be the Operator logs.
These logs can be quite noisy, and you’re likely to see errors like “the object has been modified” but these are a normal part of the reconciliation model. If the logs don’t yield anything useful, we also put debugging information into the ngrok Operator’s resources. For example, “agent endpoints” are the connections the Operator makes to ngrok, that ngrok later uses to send traffic to your application. If these have failed to create, you can check their status:
Then getting more detail about a specific endpoint:
If something has gone wrong creating the endpoint, there will be an error
message about it in this output. For example, early on in this post I had
accidentally installed the ngrok operator with an authtoken from my work account
and an API key from my personal account. The error message I found in my
agentendpoint was:
This gave me the clue I needed to realise that I had used the wrong API key. If you’re deeply stuck and can’t figure out what’s wrong, you’re welcome to file an issue and we’ll try to help you out.
Move your nginx annotations to Traffic Policy
The helloworld example has no nginx.ingress.kubernetes.io/* annotations,
but your Ingress resources probably do. The ngrok Operator ignores them, so
anything they configure, such as rewrites, redirects, rate limits, basic auth,
or IP allowlists, stops happening when traffic moves to ngrok. Move each one
before you switch DNS.
List every nginx annotation in your cluster:
Most of them map to a Traffic Policy action. For example, if your nginx
Ingress allows only your office network with
nginx.ingress.kubernetes.io/whitelist-source-range: "203.0.113.0/24", create
an NgrokTrafficPolicy that does the same with the restrict-ips action:
Then attach it to your ngrok Ingress with the ngrok.com/traffic-policy
annotation:
The ingress-nginx migration guide in the ngrok docs maps the common nginx
annotations to their ngrok equivalents. It also covers the ones that don’t
translate one-to-one: limit-rps needs a per-minute rate limit,
backend-protocol: "HTTPS" moves to an annotation on the Service, and settings
that tune NGINX itself, like buffer sizes and timeouts, have no direct
equivalent.
tls block from your nginx Ingress. The ngrok Operator ignores spec.tls and terminates TLS with the
certificate on your ngrok domain instead.Switch DNS
Now is the time to change your DNS over to point at ngrok. Every DNS registrar
will have a different way to do this, but the end result should be that your
domain is a CNAME record that points to your <gibberish>.ngrok-cname.com
address. To find it, run:
Here, 2awrvpxvfbuzgneam.5nqklokassiaw4xte.ngrok-cname.com is the address I
should use when setting up my CNAME. Yours will be different, but will end with
.ngrok-cname.com.
For folks who have opted to have ngrok automatically manage their certificates, the moment you change over to the ngrok CNAME your clients will start seeing that your domain is insecure. This will persist until ngrok provisions a certificate for the domain, which usually takes a few minutes. You can check the status of this process by clicking on the domain in your dashboard:
When your domain looks like the above, with a certificate ID near the bottom, the downtime should stop.
For those who created a TLS certificate and attached it to the domain, clients will seamlessly transition without noticing any change.
When all has settled you will be able to send a request and get a response as expected:
You can verify that this is going through ngrok by checking Traffic Inspector in your dashboard:
Your traffic is flowing through ngrok. 🎉 Once your old DNS record’s TTL has passed and no more requests reach ingress-nginx, you can clean up the old nginx Ingress resources.
Switch over to ngrok managed certificates
If you set up your own certificate during domain creation, you can now change to having ngrok manage your certificates for you. Head to the Domains page and switch to “Automated TLS certificates” like so:
ngrok will provision a certificate in the background, making sure to keep using your supplied certificate until the new one is ready. This ensures a seamless transition without downtime, and the job of making sure the certificate is valid and renewed is taken care of by ngrok.
Use the Gateway API instead of Ingress
This post keeps your Ingress resources so the migration changes as little as possible. The Kubernetes maintainers recommend the Gateway API as the long-term replacement for Ingress, and the ngrok Operator supports it too.
The Operator turns on Gateway API support when it finds the Gateway API CRDs in
your cluster. If you install the CRDs after the Operator, restart the Operator
so it picks them up. Then create a GatewayClass that points at ngrok:
To convert your existing Ingress resources, run ingress2gateway with
--providers=ingress-nginx. It translates some nginx annotations, like
rewrite-target, into Gateway API filters. Make three changes to its output
before you apply it:
- Set
gatewayClassNametongrok. ingress2gateway names the class after your old ingress class. - Change each listener to
protocol: HTTPSandport: 443. ingress2gateway writes HTTP listeners on port 80, which serve onlyhttp://URLs. The Operator can’t switch an existing listener from HTTP to HTTPS, so delete and recreate a Gateway you already applied. - Replace each
RegularExpressionpath match with aPathPrefixorExactmatch. ingress2gateway writes them for Ingress resources that userewrite-targetoruse-regex. The Operator compares a regular expression as a literal prefix, so those routes return 404.
The Gateway API guide in the ngrok docs covers listeners, routes, and attaching Traffic Policy.
Your migration checklist
To move a cluster off ingress-nginx:
- Run
kubectl get ingressclassand confirm the controller isk8s.io/ingress-nginx. - Install the ngrok Operator and register your domain. Upload your current certificate if a few minutes of TLS errors isn’t acceptable.
- Create an ngrok Ingress next to each nginx Ingress, and move its nginx annotations to Traffic Policy.
- Point your domain’s CNAME record at its
ngrok-cname.comaddress. - Switch the domain to ngrok-managed certificates.
- After your old DNS record’s TTL passes and no traffic reaches ingress-nginx, delete the old nginx Ingress resources and uninstall ingress-nginx.
After the move, the Operator only makes outbound connections to ngrok, so your cluster doesn’t need a public load balancer or open inbound ports. Every request shows up in Traffic Inspector, and Traffic Policy runs at ngrok’s edge, so a request it blocks never reaches your cluster.
If public traffic can’t pass through a third-party edge, ngrok isn’t the right fit. Pick one of the maintained controllers in the Kubernetes docs instead.
If you have any questions about what you’ve read, or you’ve tried to migrate and you’re stuck, please visit our support page or email us at support@ngrok.com.
Frequently asked questions
Yes. Kubernetes SIG Network and the Security Response Committee retired ingress-nginx in March 2026. Its GitHub repository is archived, and the project gets no new releases, bug fixes, or security patches. Existing installs keep running, but nobody fixes them.
No. ingress-nginx (kubernetes/ingress-nginx) is the community controller that Kubernetes retired. The F5 NGINX Ingress Controller (nginx/kubernetes-ingress) is a separate project that F5 still maintains. If your controller image comes from registry.k8s.io/ingress-nginx/controller, you run the retired one.
Not for long. A vulnerability found after March 2026 in ingress-nginx, the NGINX build it ships, or the Go toolchain it compiles with gets no upstream fix. The controller usually sits on the public request path and can read Secrets across the cluster, so an unpatched flaw there is high risk.
Kubernetes recommends the Gateway API, or another maintained Ingress controller if you want to keep your Ingress resources. The ngrok Operator supports both: set ingressClassName: ngrok on an Ingress, or use the ngrok GatewayClass with Gateway API resources.
Yes. Upload your current TLS certificate to ngrok. Create an ngrok Ingress next to your nginx one. Then point your domain's CNAME record at the ngrok address. After the cutover, switch the domain to ngrok-managed certificates. If you skip the upload, expect a few minutes of TLS errors while ngrok gets a certificate from Let's Encrypt.
No. Rewrites, redirects, rate limits, authentication, and IP restrictions move into a Traffic Policy that you attach with the ngrok.com/traffic-policy annotation. The ingress-nginx migration guide maps common nginx annotations to their ngrok equivalents.
To bring your own domain, yes: custom domains need the Pay-as-you-go plan or higher. To try the Operator on the free development domain ngrok assigns your account, no. See ngrok pricing.
Answers come from ngrok.com and the ngrok docs, and can be wrong. Check the linked pages before you rely on one.
Related posts
An introduction to the Kubernetes Gateway API
Take a look at key concepts of Kubernetes Gateway API, advantages over traditional approaches, and use for effective traffic control in deployments.
The ultimate guide to Kubernetes ingress
Simplify your Kubernetes ingress stack: global load balancing, DDoS protection, and flexible routing all built into ngrok’s Operator.