<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged ingress</title>
<link>https://coderlegion.com/tag/ingress</link>
<description>Powered by Question2Answer</description>
<item>
<title>Same Ingress, Different Defaults: What Changed When We Moved to HAProxy</title>
<link>https://coderlegion.com/30239/same-ingress-different-defaults-what-changed-when-we-moved-to-haproxy</link>
<description>&lt;p&gt;Over the last few weeks, we moved the customer-facing managed services on our &lt;a rel=&quot;nofollow&quot; href=&quot;https://nine.ch/en/products/kubernetes/&quot;&gt;NKE&lt;/a&gt; service clusters and &lt;a rel=&quot;nofollow&quot; href=&quot;https://nine.ch/en/products/deploio/&quot;&gt;Deploio&lt;/a&gt; from ingress-nginx to HAProxy: Grafana, Loki, ArgoCD, the container registry, Alertmanager, Tempo, OpenSearch, the Object Storage endpoints and all Deploio applications. Some clusters alone had more than 200 Ingresses.&lt;/p&gt;
&lt;p&gt;We have written about the motivation twice before. &lt;a rel=&quot;nofollow&quot; href=&quot;https://nine.ch/en/blog/retirement-of-the-ingress-nginx-project/&quot;&gt;In December&lt;/a&gt;, we explained what the retirement of ingress-nginx meant and which migration paths were open. &lt;a rel=&quot;nofollow&quot; href=&quot;https://nine.ch/en/blog/techtalk-29-our-ingress-odyssey/&quot;&gt;Our TechTalk #29&lt;/a&gt; recap covered our evaluation of Traefik, Gateway API and HAProxy, and why HAProxy became our default for classic Ingress on NKE.&lt;/p&gt;
&lt;p&gt;This post is about what happened after that decision.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.nine.ch/docs/managed-kubernetes/nke/ingress&quot;&gt;annotation mapping table in our documentation&lt;/a&gt; gets you a working Ingress on HAProxy in a few minutes. What it does not show is how the defaults shift underneath your workloads. Two ingress controllers can parse the same Ingress resource and still differ in how they talk to your backends, which status codes they send, how long they wait and how many connections they accept before refusing new ones.&lt;/p&gt;
&lt;p&gt;We found most of these differences in production, after the switch. One of them broke a customer’s website asset delivery. We are sharing them so you can check your own setup before you migrate.&lt;/p&gt;
&lt;h3 id=&quot;http-to-https-redirects&quot;&gt;HTTP to HTTPS Redirects&lt;/h3&gt;
&lt;p&gt;While comparing responses from both controllers, we noticed that plain HTTP requests were redirected with 302 Found on HAProxy, whereas nginx returned 308 Permanent Redirect.&lt;/p&gt;
&lt;p&gt;ingress-nginx sets http-redirect-code to 308 by default. haproxy-ingress defaults its corresponding ssl-redirect-code to a temporary 302. Browsers follow both, but API clients, search crawlers and intermediate caches treat them differently. A 302 allows clients to rewrite a POST into a GET on redirect, whereas a 308 preserves the request method and body.&lt;/p&gt;
&lt;p&gt;A silent downgrade to temporary redirects can break API consumers, so we set ssl-redirect-code: &quot;308&quot; globally on our managed controllers to keep the previous behaviour.&lt;/p&gt;
&lt;h3 id=&quot;connection-limits&quot;&gt;Connection Limits&lt;/h3&gt;
&lt;p&gt;During peak traffic, requests to Deploio applications slowed down and some new connections were refused.&lt;/p&gt;
&lt;p&gt;The cause was the connection limit. nginx and HAProxy calculate their ceilings differently:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nginx multiplies worker_processes by worker_connections. With&lt;br&gt;
ingress-nginx setting worker_processes to the node’s CPU count and&lt;br&gt;
worker_connections to 16384, even a modest node can handle tens of&lt;br&gt;
thousands of concurrent connections.&lt;/li&gt;
&lt;li&gt;HAProxy enforces a single global maxconn, which haproxy-ingress&lt;br&gt;
exposes as max-connections and sets to 2,000 per pod by default.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once a pod reaches that ceiling, HAProxy stops accepting new connections. They then queue in the kernel’s accept backlog, which caused the slowdown. Once the backlog is full, they are dropped.&lt;/p&gt;
&lt;p&gt;We raised max-connections to 20,000 across all managed controllers.&lt;/p&gt;
&lt;h3 id=&quot;timeouts&quot;&gt;Timeouts&lt;/h3&gt;
&lt;p&gt;Shortly after we migrated Deploio, some customers saw requests fail with 504 Gateway Timeout.&lt;/p&gt;
&lt;p&gt;haproxy-ingress defaults timeout-client and timeout-server to 50 seconds, while ingress-nginx waits 60 seconds. Ten seconds make no difference for typical web requests, but data exports or reports that reliably finished in 55 seconds suddenly failed with a 504.&lt;/p&gt;
&lt;p&gt;To avoid cutting off requests that used to succeed, our managed controllers set both timeout-client and timeout-server to 60 seconds. You can still override the timeout per Ingress with haproxy-ingress.github.io/timeout-server in place of nginx.ingress.kubernetes.io/proxy-read-timeout.&lt;/p&gt;
&lt;h3 id=&quot;upstream-http-version&quot;&gt;Upstream HTTP Version&lt;/h3&gt;
&lt;p&gt;In July, we moved an S3-compatible &lt;a rel=&quot;nofollow&quot; href=&quot;https://nine.ch/en/products/objectstorage/&quot;&gt;Object Storage&lt;/a&gt; endpoint to HAProxy. A customer served website assets through their own nginx reverse proxy in front of that endpoint. Right after the cutover, every asset request failed with 400 Bad Request.&lt;/p&gt;
&lt;p&gt;The cause: nginx’s proxy_pass talks to upstreams over HTTP/1.0 by default. ingress-nginx always spoke HTTP/1.1 to backends, regardless of the client’s protocol, and so it silently upgraded the customer’s HTTP/1.0 requests. HAProxy forwards the request with the protocol version the client sent. The Object Storage backend rejected HTTP/1.0 requests, which produced the 400 errors.&lt;/p&gt;
&lt;p&gt;The fix was for the customer to add proxy_http_version 1.1 to their reverse proxy configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location / {
    proxy_pass http://storage-endpoint;
    proxy_http_version 1.1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you run a reverse proxy in front of an NKE or Deploio Ingress, check which HTTP version it sends.&lt;/p&gt;
&lt;h3 id=&quot;basic-auth&quot;&gt;Basic Auth&lt;/h3&gt;
&lt;p&gt;After we had migrated most managed services, one of the controllers spiked to more than two full CPU cores while serving only 155 concurrent connections. Almost all traffic to that pod hit two endpoints protected by HTTP basic authentication, one of them our metrics ingestion pipeline.&lt;/p&gt;
&lt;p&gt;HAProxy verifies basic authentication via crypt(3) synchronously on every request, without caching. A bcrypt hash with a cost factor of 10, the default in most standard libraries and in Helm’s htpasswd template function, takes about 50 ms of CPU time to verify. At 50 ms per check, twenty requests per second are enough to saturate a whole CPU core.&lt;/p&gt;
&lt;p&gt;This also applies to nginx, as its auth_basic module verifies every request synchronously and caches nothing either. Because each nginx worker is a single-threaded event loop, a 50 ms verification stalls every connection on that worker. But workers are separate processes, so a controller with eight workers can verify eight passwords in parallel.&lt;/p&gt;
&lt;p&gt;HAProxy uses the thread-safe crypt_r(3) only on glibc and FreeBSD. On every other platform, including the Alpine-based image that haproxy-ingress ships, it wraps the plain crypt(3) call in a single process-wide spinlock. Only one thread can verify a password at a time, which caps the whole pod at roughly twenty verifications per second, no matter how many cores it has. And because it is a spinlock, waiting threads do not sleep. They keep their cores busy until it is their turn. That is how a pod serving 155 connections ended up using more than two cores.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.haproxy.org/3.0/configuration.html#3.4-user&quot;&gt;HAProxy manual&lt;/a&gt; warns that hashed passwords “can quickly become a major factor in HAProxy’s overall CPU consumption” and notes that musl’s hash implementations are slower than glibc’s. An upstream request to cache successful basic authentication results has been open since 2019 in &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/haproxy/haproxy/issues/370&quot;&gt;haproxy/haproxy#370&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For credentials we generate internally, we reduced the bcrypt cost to 4, where verification takes under one millisecond. This is safe because those passwords are long, machine-generated strings of 20 random characters, which makes brute-force attacks computationally infeasible regardless of the cost factor. Helm’s built-in htpasswd function always hashes with bcrypt’s default cost of 10 and offers no way to change it (&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Masterminds/sprig/blob/v3.3.0/crypto.go#L61&quot;&gt;Sprig source&lt;/a&gt;), so chart-generated credentials need to be hashed outside Helm.&lt;/p&gt;
&lt;p&gt;The annotation syntax is simpler on HAProxy. Setting haproxy-ingress.github.io/auth-secret alone enables authentication, auth-type is deprecated. The secret format stays the same: an htpasswd file stored under the auth key.&lt;/p&gt;
&lt;h3 id=&quot;x-forwarded-for&quot;&gt;X-Forwarded-For&lt;/h3&gt;
&lt;p&gt;On paper, this looks like a change in behaviour. In practice it is not one, but it does add a header.&lt;/p&gt;
&lt;p&gt;ingress-nginx replaced any incoming X-Forwarded-For with the connecting IP unless the cluster-wide compute-full-forwarded-for option was enabled, and on our shared controllers it was off.&lt;/p&gt;
&lt;p&gt;HAProxy’s option forwardfor on its own behaves differently: it appends the connecting IP, so if the client already sent an X-Forwarded-For, the backend receives both. Applications behind a CDN or reverse proxy would then see the whole proxy chain instead of the single value ingress-nginx gave them.&lt;/p&gt;
&lt;p&gt;haproxy-ingress closes that gap. Its forwardfor key defaults to add, which deletes any incoming X-Forwarded-For before HAProxy inserts the connecting IP. Your application therefore sees the same as before: a single address, the one that connected directly to the ingress, for example your CDN.&lt;/p&gt;
&lt;p&gt;What is new is one extra header. If the request already carried an X-Forwarded-For, haproxy-ingress copies its original value to X-Original-Forwarded-For before discarding it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X-Forwarded-For: &amp;lt;cdn-or-proxy-ip&amp;gt;
X-Original-Forwarded-For: &amp;lt;whatever-the-client-sent&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On its own, X-Original-Forwarded-For is untrusted input. It is copied verbatim from whoever connected to the ingress, and anyone can put anything into it.&lt;/p&gt;
&lt;p&gt;It becomes useful once your application checks who connected. X-Forwarded-For holds the address that opened the TCP connection to HAProxy, which the client cannot forge. If that address belongs to a CDN you trust, for example one of the published edge IPs of your provider, then X-Original-Forwarded-For is the header that CDN sent. Its rightmost entry is the address the CDN saw connecting to it.&lt;/p&gt;
&lt;h3 id=&quot;hsts-headers&quot;&gt;HSTS Headers&lt;/h3&gt;
&lt;p&gt;haproxy-ingress defaults its Strict-Transport-Security header to max-age=15768000 (six months). ingress-nginx sent max-age=31536000 (one year).&lt;/p&gt;
&lt;p&gt;RFC 6797 leaves the duration to the operator, and the two projects measure against different yardsticks. Six months is the minimum &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ssllabs/research/wiki/SSL-Server-Rating-Guide&quot;&gt;Qualys SSL Labs&lt;/a&gt; has required for an A+ grade since January 2014. HAProxy’s own &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.haproxy.com/blog/haproxy-and-http-strict-transport-security-hsts&quot;&gt;documentation examples&lt;/a&gt; use a value just above it, max-age=16000000, described as “a bit more than 6 months”. When HAProxy published those examples, the &lt;a rel=&quot;nofollow&quot; href=&quot;https://hstspreload.org/&quot;&gt;HSTS preload list&lt;/a&gt; accepted a max-age shorter than one year. It has since raised the minimum to one year.&lt;/p&gt;
&lt;p&gt;For standard web traffic, this has no functional impact. If your domain is on the HSTS preload list, however, six months fails validation. We therefore override the haproxy-ingress default and send max-age=31536000 (one year), the same value ingress-nginx did.&lt;/p&gt;
&lt;p&gt;If you need a different duration, set spec.forProvider.hsts.maxAge on the IngressHAProxy resource (a duration, for example 8760h). For a single Ingress, set haproxy-ingress.github.io/hsts-max-age (in seconds).&lt;/p&gt;
&lt;h3 id=&quot;request-buffering&quot;&gt;Request Buffering&lt;/h3&gt;
&lt;p&gt;Under ingress-nginx, high-volume write endpoints produced warnings like this in the controller logs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[warn] a client request body is buffered to a temporary file /tmp/nginx/client-body/0000000123
  request: &quot;POST /loki/api/v1/push HTTP/1.1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A while back, one node in our service cluster ran at 98% CPU, with its nginx pod using 1.4 cores mainly to write and clean up temporary disk buffers. On nginx, the fix was to set nginx.ingress.kubernetes.io/proxy-request-buffering: &quot;off&quot; on write-heavy Ingresses such as Loki, Promscale and container registries.&lt;/p&gt;
&lt;p&gt;HAProxy streams request bodies to backends by default. There is no equivalent buffering annotation, because HAProxy does not write incoming payloads to disk.&lt;/p&gt;
&lt;h3 id=&quot;where-this-leaves-you&quot;&gt;Where This Leaves You&lt;/h3&gt;
&lt;p&gt;If you still use our managed ingress-nginx on NKE: as announced, support ended on 1 October 2026. HAProxy is available for every NKE cluster, and our &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.nine.ch/docs/managed-kubernetes/nke/ingress&quot;&gt;migration documentation&lt;/a&gt; is the place to start.&lt;/p&gt;
&lt;p&gt;If you are unsure how one of these differences affects your workloads, write to us at &lt;em&gt;Emails are not allowed&lt;/em&gt; and we will look at it with you.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30239/same-ingress-different-defaults-what-changed-when-we-moved-to-haproxy</guid>
<pubDate>Thu, 08 Oct 2026 08:23:47 +0000</pubDate>
</item>
<item>
<title>After NGINX Ingress Controller: Alternatives and Migration Guide</title>
<link>https://coderlegion.com/15638/after-nginx-ingress-controller-alternatives-and-migration-guide</link>
<description>
&lt;p&gt;If you manage Kubernetes clusters in production, the last 18 months have been uncomfortable. Two of the most widely deployed NGINX-based Ingress Controllers have faced critical security vulnerabilities, deprecation announcements, and shifting maintenance responsibilities &amp;mdash; all while the Kubernetes project accelerates its push toward a new traffic management standard. This is not a drill. Teams running ingress-nginx or the F5/NGINX Ingress Controller need a clear picture of what changed, what it means for their clusters, and what their realistic options are going forward.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;First, Clear the Confusion: There Are Two NGINX Ingress Controllers&lt;/h2&gt;



&lt;p&gt;One of the most persistent sources of confusion in the Kubernetes networking space is that there are two completely different projects both called &amp;ldquo;NGINX Ingress Controller,&amp;rdquo; maintained by different organizations, with different architectures and different licensing.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;ingress-nginx (kubernetes/ingress-nginx)&lt;/h3&gt;



&lt;p&gt;This is the community-maintained controller under the Kubernetes project umbrella, hosted at &lt;code&gt;github.com/kubernetes/ingress-nginx&lt;/code&gt;. It uses the open-source NGINX as its data plane, configured via Lua scripting and dynamically generated &lt;code&gt;nginx.conf&lt;/code&gt; files. This is the controller most teams end up with when they follow the official Kubernetes documentation or install from the Helm chart referenced in the ingress guide. It is free, open-source, and until recently was considered the default choice.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;NGINX Ingress Controller (nginxinc/kubernetes-ingress)&lt;/h3&gt;



&lt;p&gt;This is the commercial and open-source controller maintained by F5/NGINX, hosted at &lt;code&gt;github.com/nginxinc/kubernetes-ingress&lt;/code&gt;. It also supports NGINX Plus (the commercial version with enhanced features like active health checks, JWT authentication, and advanced load balancing). The architecture is different &amp;mdash; it uses native NGINX APIs rather than the Lua-heavy approach &amp;mdash; and it targets enterprise customers looking for support contracts and advanced capabilities.&lt;/p&gt;



&lt;p&gt;These two controllers are not interchangeable. Configuration annotations differ, Helm chart values differ, and behavior under edge cases differs substantially. Understanding which one your cluster runs is the necessary starting point for any decision about migration.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# Check which NGINX IC you are actually running
kubectl get pods -n ingress-nginx -o jsonpath='{.items[*].spec.containers[*].image}'

# Community controller image looks like:
# registry.k8s.io/ingress-nginx/controller:v1.x.x

# F5/NGINX controller image looks like:
# nginx/nginx-ingress:x.x.x  or  private-registry.nginx.com/nginx-ic/nginx-plus-ingress:x.x.x&lt;/code&gt;&lt;/pre&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Actually Happened: A Timeline of Disruption&lt;/h2&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;The ingress-nginx CVEs (2024)&lt;/h3&gt;



&lt;p&gt;In March 2024, security researchers disclosed a set of critical vulnerabilities in &lt;code&gt;ingress-nginx&lt;/code&gt; under the collective name &lt;strong&gt;IngressNightmare&lt;/strong&gt; (CVE-2025-1097, CVE-2025-1098, CVE-2025-1974, CVE-2025-24514). The most severe of these, rated CVSS 9.8, allowed unauthenticated remote code execution against the ingress-nginx admission webhook. An attacker with network access to the admission controller could craft a malicious Ingress object to inject arbitrary NGINX configuration, ultimately achieving code execution in the controller pod &amp;mdash; which in many clusters runs with elevated permissions and access to service account tokens across namespaces.&lt;/p&gt;



&lt;p&gt;The vulnerabilities affected the vast majority of ingress-nginx deployments in the wild. Wiz Research, which discovered and disclosed the issues, estimated that approximately 43% of cloud environments were exposed. Patches were released in versions 1.11.5 and 1.12.1, but the incident forced uncomfortable questions about the controller&amp;rsquo;s security posture and the architecture decisions (particularly the admission webhook design) that made it possible.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Maintenance Concerns in ingress-nginx&lt;/h3&gt;



&lt;p&gt;Beyond the CVEs, the ingress-nginx project has faced ongoing concerns about maintainer bandwidth. The project is maintained by a small group of volunteers and relies heavily on community contributions. Issue response times slowed, pull requests aged, and the pace of feature development declined relative to alternatives. For a component as critical as the cluster ingress layer, this created legitimate concern about long-term sustainability without corporate backing or broader contributor growth.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;F5/NGINX Deprecation Announcement&lt;/h3&gt;



&lt;p&gt;On the commercial side, F5/NGINX announced in early 2025 that the &lt;code&gt;nginxinc/kubernetes-ingress&lt;/code&gt; controller &amp;mdash; particularly its open-source tier &amp;mdash; would undergo significant changes. F5 signaled a strategic shift toward NGINX Gateway Fabric, their implementation of the Kubernetes Gateway API specification. The message was clear: investment in the Ingress-based controller would be reduced, and customers were encouraged to plan migrations toward Gateway API-native solutions.&lt;/p&gt;



&lt;p&gt;For teams running NGINX Plus-based ingress with support contracts, this was a significant business concern. The product they had licensed and standardized on was being steered toward end-of-life on the Ingress API, even if exact timelines remained somewhat ambiguous in the initial announcements.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Real Impact on Production Clusters&lt;/h2&gt;



&lt;p&gt;The practical consequences depend heavily on which controller you run and how your clusters are configured. Here is an honest assessment:&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Immediate Security Risk&lt;/h3&gt;



&lt;p&gt;If you run ingress-nginx and have not patched to 1.11.5+ or 1.12.1+, your admission webhook is a critical attack surface. Patching is non-negotiable and should have happened already. The admission webhook can be disabled if you are not using it for validation (many teams are not), which significantly reduces the attack surface while you plan a longer-term migration.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# Check your current ingress-nginx version
kubectl get deployment ingress-nginx-controller -n ingress-nginx \
  -o jsonpath='{.spec.template.spec.containers[0].image}'

# Verify admission webhook is configured
kubectl get validatingwebhookconfigurations | grep ingress

# If you need to disable the webhook temporarily (reduces but does not eliminate risk):
kubectl delete validatingwebhookconfiguration ingress-nginx-admission&lt;/code&gt;&lt;/pre&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Operational Uncertainty&lt;/h3&gt;



&lt;p&gt;Even after patching, the underlying questions remain. Teams are now asking: should we invest in hardening and tuning ingress-nginx knowing it may not be the strategic direction? Should we migrate now when it is our choice, rather than later when it may be forced? For NGINX IC customers, they are evaluating whether their licensing costs justify continued investment in a product being steered toward deprecation.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Configuration Migration Complexity&lt;/h3&gt;



&lt;p&gt;The real cost of migration is in the annotation-heavy configurations that accumulate over time. Teams that have built complex routing logic using &lt;code&gt;nginx.ingress.kubernetes.io/*&lt;/code&gt; annotations &amp;mdash; custom headers, rate limiting, auth snippets, rewrite rules, canary traffic splitting &amp;mdash; face significant rework when switching controllers. This is the primary reason many teams are reluctant to move despite clear signals that a transition is coming.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Alternatives: An Honest Evaluation&lt;/h2&gt;



&lt;p&gt;There is no shortage of Ingress controller options. The question is which alternatives are mature enough for production workloads at scale, and what trade-offs each brings.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Traefik&lt;/h3&gt;



&lt;p&gt;Traefik Proxy (and its Kubernetes-native version via Traefik Hub) has emerged as the most popular alternative for teams leaving ingress-nginx. It supports the standard Kubernetes Ingress API for drop-in compatibility, its own &lt;code&gt;IngressRoute&lt;/code&gt; CRDs for advanced features, and Kubernetes Gateway API. It is written in Go, has strong TLS automation via Let&amp;rsquo;s Encrypt, and has excellent observability with built-in metrics and a real-time dashboard.&lt;/p&gt;



&lt;p&gt;Trade-offs: Traefik&amp;rsquo;s configuration model is different enough from NGINX that complex routing logic requires rethinking rather than translating. Performance under very high connection counts is generally good but NGINX has a longer track record in extreme-scale deployments. The commercial offering (Traefik Hub) adds API gateway capabilities but introduces vendor dependency.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Envoy Gateway&lt;/h3&gt;



&lt;p&gt;Envoy Gateway is now a CNCF project and implements the Kubernetes Gateway API natively using Envoy as its data plane. This is arguably the most strategically aligned option for teams that want to bet on the future of Kubernetes networking. Envoy is battle-tested (it powers Istio, Contour, and large-scale service meshes at companies like Lyft and Google), and the Gateway API implementation is comprehensive and actively developed.&lt;/p&gt;



&lt;p&gt;Trade-offs: Envoy Gateway is relatively young as a standalone project. Teams unfamiliar with Envoy will face a steeper learning curve for debugging and custom configuration. The operational model differs significantly from NGINX-based controllers. However, for greenfield deployments or teams willing to invest in the transition, this is a strong forward-looking choice.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Cilium Gateway API&lt;/h3&gt;



&lt;p&gt;If your cluster already runs Cilium as the CNI, enabling Gateway API support is a natural evolution. Cilium&amp;rsquo;s Gateway API implementation leverages eBPF for high-performance packet processing, avoiding the overhead of userspace proxy hops entirely. It is deeply integrated with Cilium&amp;rsquo;s network policy model and observability stack (Hubble).&lt;/p&gt;



&lt;p&gt;Trade-offs: This option is only relevant if you are already committed to Cilium as your CNI, or are willing to make that switch simultaneously. Migrating both the CNI and the ingress layer at the same time is a significant operational risk. For Cilium shops, however, this consolidates complexity and provides excellent performance and observability.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;HAProxy Ingress&lt;/h3&gt;



&lt;p&gt;HAProxy Ingress Controller is maintained by the HAProxy Technologies team and has a strong reputation for raw performance and precise traffic control. It supports both Ingress and Gateway API and has a long track record in high-throughput production environments. For teams with existing HAProxy expertise, it provides a familiar mental model for load balancing configuration.&lt;/p&gt;



&lt;p&gt;Trade-offs: Smaller community than Traefik or NGINX. Less ecosystem tooling and fewer tutorials. Best suited for teams that specifically want HAProxy&amp;rsquo;s capabilities (fine-grained connection management, advanced health checking, TCP/HTTP mode flexibility) rather than as a default choice.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Kong Ingress Controller&lt;/h3&gt;



&lt;p&gt;Kong bridges the gap between an Ingress controller and a full API gateway. It supports Ingress and Gateway API resources alongside its own Kong-native plugin system for authentication, rate limiting, transformation, and observability. For teams that need API gateway capabilities rather than pure L7 routing, Kong provides a unified platform.&lt;/p&gt;



&lt;p&gt;Trade-offs: Kong adds operational complexity. Running Kong requires either a PostgreSQL database (DB-mode) or careful management of declarative configuration (DB-less mode). The plugin ecosystem is powerful but introduces additional configuration surface. For teams that just need ingress routing, Kong may be more than necessary. For teams building API platforms, it is worth the overhead.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Istio Gateway&lt;/h3&gt;



&lt;p&gt;Istio&amp;rsquo;s ingress gateway (now aligned with Gateway API via its Kubernetes Gateway API integration) provides entry-point traffic management as part of a full service mesh. If your organization is planning or running Istio for east-west traffic, using Istio&amp;rsquo;s gateway for north-south traffic creates a unified data plane (Envoy) and consistent observability across all service communication.&lt;/p&gt;



&lt;p&gt;Trade-offs: Istio is a serious operational commitment. The control plane overhead, the learning curve, and the impact on pod scheduling and sidecar management are significant. Choosing Istio purely for ingress replacement is like buying a race car because you needed a vehicle with good brakes. Consider this path only if service mesh capabilities are on your roadmap.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;NGINX Gateway Fabric (F5&amp;rsquo;s Gateway API implementation)&lt;/h3&gt;



&lt;p&gt;F5/NGINX is building NGINX Gateway Fabric as their strategic forward path &amp;mdash; an NGINX-based implementation of the Kubernetes Gateway API. For teams heavily invested in NGINX and wanting to stay in that ecosystem while moving to Gateway API, this provides a migration path within familiar territory. It is still maturing but represents where F5 is putting its development resources.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Comparison Matrix&lt;/h2&gt;



&lt;figure class=&quot;wp-block-table&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Controller&lt;/th&gt;&lt;th&gt;Ingress API&lt;/th&gt;&lt;th&gt;Gateway API&lt;/th&gt;&lt;th&gt;Maturity&lt;/th&gt;&lt;th&gt;Best For&lt;/th&gt;&lt;th&gt;Complexity&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ingress-nginx&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Partial&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;Existing deployments, familiar config&lt;/td&gt;&lt;td&gt;Low&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Traefik&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;General purpose, rapid migration&lt;/td&gt;&lt;td&gt;Low-Medium&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Envoy Gateway&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes (native)&lt;/td&gt;&lt;td&gt;Medium&lt;/td&gt;&lt;td&gt;Greenfield, future-aligned&lt;/td&gt;&lt;td&gt;Medium&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cilium Gateway&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Medium&lt;/td&gt;&lt;td&gt;Cilium CNI clusters&lt;/td&gt;&lt;td&gt;Low (if Cilium)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HAProxy Ingress&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;High-throughput, HAProxy expertise&lt;/td&gt;&lt;td&gt;Medium&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kong&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;API gateway requirements&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Istio Gateway&lt;/td&gt;&lt;td&gt;Via Gateway API&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;Service mesh adopters&lt;/td&gt;&lt;td&gt;Very High&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;NGINX Gateway Fabric&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes (native)&lt;/td&gt;&lt;td&gt;Low-Medium&lt;/td&gt;&lt;td&gt;NGINX shops moving to Gateway API&lt;/td&gt;&lt;td&gt;Medium&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Gateway API: The Strategic Direction You Cannot Ignore&lt;/h2&gt;



&lt;p&gt;The Kubernetes Gateway API is not simply &amp;ldquo;Ingress v2.&amp;rdquo; It is a fundamentally richer traffic management model designed to address the limitations that drove teams to annotation-based workarounds for the past several years. Understanding it is essential regardless of which controller you choose, because the ecosystem is clearly converging on it.&lt;/p&gt;



&lt;p&gt;The core resource hierarchy consists of &lt;strong&gt;GatewayClass&lt;/strong&gt; (defines a type of gateway, created by infrastructure providers), &lt;strong&gt;Gateway&lt;/strong&gt; (a specific instance of a listener configuration, typically managed by platform teams), and &lt;strong&gt;HTTPRoute&lt;/strong&gt;, &lt;strong&gt;TCPRoute&lt;/strong&gt;, &lt;strong&gt;GRPCRoute&lt;/strong&gt;, and other route resources (managed by application teams). This separation of concerns maps cleanly onto organizational roles &amp;mdash; infrastructure teams control the gateway, application teams control their routing rules.&lt;/p&gt;



&lt;pre class=&quot;wp-block-code&quot;&gt;&lt;code&gt;# Example Gateway API resources replacing an ingress-nginx Ingress
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: infra
spec:
  gatewayClassName: nginx  # or envoy, traefik, cilium, etc.
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - name: wildcard-tls
    allowedRoutes:
      namespaces:
        from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
  namespace: my-app-namespace
spec:
  parentRefs:
  - name: production-gateway
    namespace: infra
  hostnames:
  - &quot;app.example.com&quot;
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: my-api-service
      port: 8080
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: my-frontend-service
      port: 3000&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Gateway API reached v1.0 (GA for HTTPRoute and Gateway) in October 2023, and v1.1 followed in 2024 with GRPCRoute graduation and expanded features. The project has broad support across controllers (Traefik, Envoy Gateway, Cilium, NGINX Gateway Fabric, Kong, Istio, and others all implement it). The Ingress API is not being removed from Kubernetes, but new feature development is effectively frozen &amp;mdash; Gateway API is where capabilities like traffic weighting, header manipulation, request mirroring, and backend protocol configuration are being built.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Decision Framework: Stay, Migrate, or Evaluate?&lt;/h2&gt;



&lt;p&gt;There is no universal right answer. The following framework helps teams make a context-appropriate decision rather than following hype or panic.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Stay on ingress-nginx if:&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;You have patched to 1.11.5+ or 1.12.1+ and have disabled or hardened the admission webhook&lt;/li&gt;&lt;li&gt;Your cluster is stable, heavily annotation-dependent, and migration cost outweighs risk&lt;/li&gt;&lt;li&gt;You have internal NGINX expertise and can take ownership of monitoring the project&amp;rsquo;s maintenance health&lt;/li&gt;&lt;li&gt;Your organization has a short-term horizon (decommissioning or major platform change within 12-18 months)&lt;/li&gt;&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Migrate now if:&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;You are running the F5/NGINX IC with a support contract that is being deprecated&lt;/li&gt;&lt;li&gt;Your cluster has moderate annotation complexity and you have engineering cycles available&lt;/li&gt;&lt;li&gt;You are planning a major Kubernetes version upgrade or cluster rebuild &amp;mdash; do it at the same time&lt;/li&gt;&lt;li&gt;Your security team has flagged the CVE history as unacceptable for your risk profile&lt;/li&gt;&lt;li&gt;You are building a new cluster or platform team and want to standardize on Gateway API from the start&lt;/li&gt;&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Evaluate before committing if:&lt;/h3&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;&lt;li&gt;Your workloads have complex traffic requirements (WebSockets, gRPC, canary deployments, header-based routing) that differ significantly across controllers&lt;/li&gt;&lt;li&gt;You are considering Gateway API but the specific &lt;/li&gt;&lt;/ul&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/15638/after-nginx-ingress-controller-alternatives-and-migration-guide</guid>
<pubDate>Thu, 23 Apr 2026 22:56:21 +0000</pubDate>
</item>
<item>
<title>Building a Kubernetes Migration Framework: Lessons from Ingress-NGINX</title>
<link>https://coderlegion.com/12246/building-a-kubernetes-migration-framework-lessons-from-ingress-nginx</link>
<description>
&lt;a href=&quot;https://alexandre-vazquez.com/building-a-kubernetes-migration-framework-lessons-from-ingress-nginx/&quot; title=&quot;Building a Kubernetes Migration Framework: Lessons from Ingress-NGINX&quot; rel=&quot;nofollow&quot;&gt;&lt;img width=&quot;1536&quot; height=&quot;1024&quot; src=&quot;https://mlk5tkvgfeoa.i.optimole.com/cb:s8WW.68/w:1536/h:1024/q:mauto/f:best/https://alexandre-vazquez.com/wp-content/uploads/2026/02/k8s-ingress-migration.avif&quot; class=&quot;webfeedsFeaturedVisual wp-post-image&quot; alt=&quot;Building a Kubernetes Migration Framework: Lessons from Ingress-NGINX&quot; style=&quot;display: block; margin: auto; margin-bottom: 5px;max-width: 100%;&quot; title=&quot;Building a Kubernetes Migration Framework: Lessons from Ingress-NGINX 4&quot;&gt;&lt;/a&gt;
&lt;p&gt;The recent announcement regarding the &lt;a href=&quot;https://www.reddit.com/r/kubernetes/comments/1qw534a/understanding_the_ingressnginx_deprecation_before/&quot; target=&quot;_blank&quot; rel=&quot;noopener nofollow&quot;&gt;deprecation of the Ingress-NGINX controller&lt;/a&gt; sent a ripple through the Kubernetes community. For many organizations, it&amp;rsquo;s the first major deprecation of a foundational, widely-adopted ecosystem component. While the immediate reaction is often tactical&amp;mdash;&amp;rdquo;What do we replace it with?&amp;rdquo;&amp;mdash;the more valuable long-term question is strategic: &amp;ldquo;How do we systematically manage this and future migrations?&amp;rdquo;&lt;/p&gt;



&lt;p&gt;This event isn&amp;rsquo;t an anomaly; it&amp;rsquo;s a precedent. As Kubernetes matures, core add-ons, APIs, and patterns will evolve or sunset. Platform engineering teams need a repeatable, low-risk framework for navigating these changes. Drawing from the Ingress-NGINX transition and established deployment management principles, we can abstract a robust &lt;strong&gt;Kubernetes Migration Framework&lt;/strong&gt; applicable to any major component, from service meshes to CSI drivers.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Why Ad-Hoc Migrations Fail in Production&lt;/h2&gt;



&lt;p&gt;Attempting a &amp;ldquo;big bang&amp;rdquo; replacement or a series of manual, one-off changes is a recipe for extended downtime, configuration drift, and undetected regression. Production Kubernetes environments are complex systems with deep dependencies:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Interdependent Workloads:&lt;/strong&gt; Multiple applications often share the same ingress controller, relying on specific annotations, custom snippets, or behavioral quirks.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Automation and GitOps Dependencies:&lt;/strong&gt; Helm charts, Kustomize overlays, and ArgoCD/Flux manifests are tightly coupled to the existing component&amp;rsquo;s API and schema.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Observability and Security Integration:&lt;/strong&gt; Monitoring dashboards, logging parsers, and security policies are tuned for the current implementation.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Knowledge Silos:&lt;/strong&gt; Tribal knowledge about workarounds and specific configurations isn&amp;rsquo;t documented.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;A structured framework mitigates these risks by enforcing discipline, creating clear validation gates, and ensuring the capability to roll back at any point.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The Four-Phase Kubernetes Migration Framework&lt;/h2&gt;



&lt;p&gt;This framework decomposes the migration into four distinct phases: Assessment, Parallel Run, Cutover, and Decommission. Each phase has defined inputs, activities, and exit criteria.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Phase 1: Deep Assessment &amp;amp; Dependency Mapping&lt;/h3&gt;



&lt;p&gt;Before writing a single line of new configuration, understand the full scope. The goal is to move from &amp;ldquo;we use Ingress-NGINX&amp;rdquo; to a precise inventory of &lt;em&gt;how&lt;/em&gt; it&amp;rsquo;s used.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Inventory All Ingress Resources:&lt;/strong&gt; Use &lt;code&gt;kubectl get ingress --all-namespaces&lt;/code&gt; as a starting point, but go deeper.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Analyze Annotation Usage:&lt;/strong&gt; Script an analysis to catalog every annotation in use (e.g., &lt;code&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/code&gt;, &lt;code&gt;nginx.ingress.kubernetes.io/configuration-snippet&lt;/code&gt;). This reveals functional dependencies.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Map to Backend Services:&lt;/strong&gt; For each Ingress, identify the backend Services and Namespaces. This highlights critical applications and potential blast radius.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Review Customizations:&lt;/strong&gt; Document any custom ConfigMaps for main NGINX configuration, custom template patches, or modifications to the controller deployment itself.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Evaluate Alternatives:&lt;/strong&gt; Based on the inventory, evaluate candidate replacements (e.g., &lt;a rel=&quot;nofollow&quot; href=&quot;https://alexandre-vazquez.com/kubernetes-gateway-api-versions-compatibility-guide/&quot; data-type=&quot;post&quot; data-id=&quot;6712&quot;&gt;Gateway API&lt;/a&gt; with a compatible implementation, another Ingress controller like &lt;a href=&quot;https://github.com/emissary-ingress/emissary&quot; data-type=&quot;link&quot; data-id=&quot;https://github.com/emissary-ingress/emissary&quot; target=&quot;_blank&quot; rel=&quot;noopener nofollow&quot;&gt;Emissary-ingress&lt;/a&gt; or Traefik). The &lt;a href=&quot;https://docs.cloud.google.com/architecture/migrating-containers-kubernetes-gke&quot; target=&quot;_blank&quot; rel=&quot;noopener nofollow&quot;&gt;Google Cloud migration framework&lt;/a&gt; provides a useful decision tree for ingress-specific migrations.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;The output of this phase is a migration manifesto: a concrete list of what needs to be converted, grouped by complexity and criticality.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Phase 2: Phased Rollout &amp;amp; Parallel Run&lt;/h3&gt;



&lt;p&gt;This is the core of a low-risk migration. Instead of replacing, you run the new and old systems in parallel, shifting traffic gradually. For ingress, this often means installing the new controller alongside the old one.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Dual Installation:&lt;/strong&gt; Deploy the new ingress controller in the same cluster, configured with a distinct ingress class (e.g., &lt;code&gt;ingressClassName: gateway&lt;/code&gt; vs. &lt;code&gt;nginx&lt;/code&gt;).&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Create Canary Ingress Resources:&lt;/strong&gt; For a low-risk application, create a parallel Ingress or Gateway resource pointing to the new controller. Use techniques like &lt;a href=&quot;https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/&quot; target=&quot;_blank&quot; rel=&quot;noopener nofollow&quot;&gt;managed deployments with canary patterns&lt;/a&gt; to control exposure.&lt;br&gt;&lt;pre&gt;&lt;code&gt;# Example: A new Gateway API HTTPRoute for a canary service&lt;br&gt;apiVersion: gateway.networking.k8s.io/v1&lt;br&gt;kind: HTTPRoute&lt;br&gt;metadata:&lt;br&gt;  name: app-canary&lt;br&gt;spec:&lt;br&gt;  parentRefs:&lt;br&gt;  - name: company-gateway&lt;br&gt;  rules:&lt;br&gt;  - backendRefs:&lt;br&gt;    - name: app-service&lt;br&gt;      port: 8080&lt;br&gt;      weight: 10 # Start with 10% of traffic&lt;/code&gt;&lt;/pre&gt;&lt;br&gt;&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Validate Equivalency:&lt;/strong&gt; Use traffic mirroring (if supported) or direct synthetic testing against both ingress paths. Compare logs, response headers, latency, and error rates.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Iterate and Expand:&lt;/strong&gt; Gradually increase traffic weight or add more applications to the new stack, group by group, based on the assessment from Phase 1.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This phase relies heavily on your observability stack. Dashboards comparing error rates, latency (p50, p99), and throughput between the old and new paths are essential.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Phase 3: Validation &amp;amp; Automated Cutover&lt;/h3&gt;



&lt;p&gt;The cutover is not a manual event. It&amp;rsquo;s the final step in a validation process.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Define Validation Tests:&lt;/strong&gt; Create a suite of tests that must pass before full cutover. This includes:
&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;Smoke tests for all critical user journeys.&lt;/li&gt;



&lt;li&gt;Load tests to verify performance under expected traffic patterns.&lt;/li&gt;



&lt;li&gt;Security scan validation (e.g., no unintended ports open).&lt;/li&gt;



&lt;li&gt;Compliance checks (e.g., specific headers are present).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Automate the Switch:&lt;/strong&gt; For each application, the cutover is ultimately a change in its Ingress or Gateway resource. This should be done via your GitOps pipeline. Update the source manifests (e.g., change the &lt;code&gt;ingressClassName&lt;/code&gt;), merge, and let automation apply it. This ensures the state is declarative and recorded.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Maintain Rollback Capacity:&lt;/strong&gt; The old system must remain operational and routable (with reduced capacity) during this phase. The GitOps rollback is simply reverting the manifest change.&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;Phase 4: Observability &amp;amp; Decommission&lt;/h3&gt;



&lt;p&gt;Once all traffic is successfully migrated and validated over a sustained period (e.g., 72 hours), you can decommission the old component.&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Monitor Aggressively:&lt;/strong&gt; Keep a close watch on all key metrics for at least one full business cycle (a week).&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Remove Old Resources:&lt;/strong&gt; Delete the old controller&amp;rsquo;s Deployment, Service, ConfigMaps, and CRDs (if no longer needed).&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Clean Up Auxiliary Artifacts:&lt;/strong&gt; Remove old RBAC bindings, service accounts, and any custom monitoring alerts or dashboards specific to the old component.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Document Lessons Learned:&lt;/strong&gt; Update runbooks and architecture diagrams. Note any surprises, gaps in the process, or validation tests that were particularly valuable.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Key Principles for a Resilient Framework&lt;/h2&gt;



&lt;p&gt;Beyond the phases, these principles should guide your framework&amp;rsquo;s design:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
&lt;li&gt;&lt;strong&gt;Always Maintain Rollback Capability:&lt;/strong&gt; Every step should be reversible with minimal disruption. This is a core tenet of &lt;a href=&quot;https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/&quot; target=&quot;_blank&quot; rel=&quot;noopener nofollow&quot;&gt;managing Kubernetes deployments&lt;/a&gt;.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Leverage GitOps for State Management:&lt;/strong&gt; All desired state changes (Ingress resources, controller deployments) must flow through version-controlled manifests. This provides an audit trail, consistency, and the simplest rollback mechanism (git revert).&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Validate with Production Traffic Patterns:&lt;/strong&gt; Synthetic tests are insufficient. Use canary weights and traffic mirroring to validate with real user traffic in a controlled manner.&lt;/li&gt;



&lt;li&gt;&lt;strong&gt;Communicate Transparently:&lt;/strong&gt; Platform teams should maintain a clear migration status page for internal stakeholders, showing which applications have been migrated, which are in progress, and the overall timeline.&lt;/li&gt;
&lt;/ul&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Conclusion: Building a Migration-Capable Platform&lt;/h2&gt;



&lt;p&gt;The deprecation of Ingress-NGINX is a wake-up call. The next major change is a matter of &amp;ldquo;when,&amp;rdquo; not &amp;ldquo;if.&amp;rdquo; By investing in a structured migration framework now, platform teams transform a potential crisis into a manageable, repeatable operational procedure.&lt;/p&gt;



&lt;p&gt;This framework&amp;mdash;&lt;strong&gt;Assess, Run in Parallel, Validate, and Decommission&lt;/strong&gt;&amp;mdash;abstracts the specific lessons from the ingress migration into a generic pattern. It can be applied to migrating from PodSecurityPolicies to Pod Security Standards, from a deprecated CSI driver, or from one service mesh to another. The tools (GitOps, canary deployments, observability) are already in your stack. The value is in stitching them together into a disciplined process that ensures platform evolution doesn&amp;rsquo;t compromise platform stability.&lt;/p&gt;



&lt;p&gt;Start by documenting this framework as a runbook template. Then, apply it to your next significant component update, even a minor one, to refine the process. When the next major deprecation announcement lands in your inbox, you&amp;rsquo;ll be ready.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/12246/building-a-kubernetes-migration-framework-lessons-from-ingress-nginx</guid>
<pubDate>Sat, 28 Feb 2026 05:30:04 +0000</pubDate>
</item>
<item>
<title>Resolving Kubernetes Ingress Issues: Limitations and Gateway Insights</title>
<link>https://coderlegion.com/11559/resolving-kubernetes-ingress-issues-limitations-and-gateway-insights</link>
<description>
&lt;a href=&quot;https://alexandre-vazquez.com/kubernetes-ingress-issues/&quot; title=&quot;Resolving Kubernetes Ingress Issues: Limitations and Gateway Insights&quot; rel=&quot;nofollow&quot;&gt;&lt;img width=&quot;1536&quot; height=&quot;1024&quot; src=&quot;https://mlk5tkvgfeoa.i.optimole.com/cb:s8WW.68/w:1536/h:1024/q:mauto/f:best/https://alexandre-vazquez.com/wp-content/uploads/2025/11/ingress.jpeg&quot; class=&quot;webfeedsFeaturedVisual wp-post-image&quot; alt=&quot;Resolving Kubernetes Ingress Issues: Limitations and Gateway Insights&quot; style=&quot;display: block; margin: auto; margin-bottom: 5px;max-width: 100%;&quot; sizes=&quot;(max-width: 1536px) 100vw, 1536px&quot; title=&quot;Resolving Kubernetes Ingress Issues: Limitations and Gateway Insights 18&quot;&gt;&lt;/a&gt;
&lt;h2 class=&quot;wp-block-heading&quot;&gt;Introduction&lt;/h2&gt;



&lt;p&gt;&lt;strong&gt;Ingresses&lt;/strong&gt; have been, since the early versions of Kubernetes, the most common way to expose applications to the outside. Although their initial design was simple and elegant, the success of Kubernetes and the growing complexity of use cases have turned Ingress into a problematic piece: limited, inconsistent between vendors, and difficult to govern in enterprise environments.&lt;/p&gt;



&lt;p&gt;In this article, we analyze &lt;strong&gt;why Ingresses have become a constant source of friction&lt;/strong&gt;, how different Ingress Controllers have influenced this situation, and why more and more organizations are considering alternatives like &lt;strong&gt;Gateway API&lt;/strong&gt;.&lt;br&gt;&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;What Ingresses are and why they were designed this way&lt;/h2&gt;



&lt;p&gt;The Ingress ecosystem revolves around two main resources:&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;img src=&quot;https://s.w.org/images/core/emoji/16.0.1/72x72/1f3f7.png&quot; alt=&quot;????&quot; class=&quot;wp-smiley&quot; style=&quot;height: 1em; max-height: 1em;&quot;&gt; IngressClass&lt;/h3&gt;



&lt;p&gt;Defines which controller will manage the associated Ingresses. Its scope is &lt;strong&gt;cluster-wide&lt;/strong&gt;, so it is usually managed by the platform team.&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;img src=&quot;https://s.w.org/images/core/emoji/16.0.1/72x72/1f310.png&quot; alt=&quot;????&quot; class=&quot;wp-smiley&quot; style=&quot;height: 1em; max-height: 1em;&quot;&gt; Ingress&lt;/h3&gt;



&lt;p&gt;It is the resource that developers use to expose a service. It allows defining routes, domains, TLS certificates, and little more.&lt;/p&gt;



&lt;p&gt;Its specification &lt;strong&gt;is minimal by design&lt;/strong&gt;, which allowed for rapid adoption, but also laid the foundation for current problems.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The problem: a standard too simple for complex needs&lt;/h2&gt;



&lt;p&gt;As Kubernetes became an enterprise standard, users wanted to replicate advanced configurations of traditional proxies: rewrites, timeouts, custom headers, CORS, etc.&lt;br&gt;But &lt;strong&gt;Ingress did not provide native support&lt;/strong&gt; for all this.&lt;/p&gt;



&lt;p&gt;Vendors reacted&amp;hellip; and chaos was born.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Annotations vs CRDs: two incompatible paths&lt;/h2&gt;



&lt;p&gt;Different Ingress Controllers have taken very different paths to add advanced capabilities:&lt;/p&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;img src=&quot;https://s.w.org/images/core/emoji/16.0.1/72x72/1f4dd.png&quot; alt=&quot;????&quot; class=&quot;wp-smiley&quot; style=&quot;height: 1em; max-height: 1em;&quot;&gt; Annotations (NGINX, HAProxy&amp;hellip;)&lt;/h3&gt;



&lt;p&gt;Advantages:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Flexible and easy to use&lt;/li&gt;
	&lt;li&gt;Directly in the Ingress resource&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Disadvantages:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Hundreds of proprietary annotations&lt;/li&gt;
	&lt;li&gt;Fragmented documentation&lt;/li&gt;
	&lt;li&gt;Non-portable configurations between vendors&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;h3 class=&quot;wp-block-heading&quot;&gt;&lt;img src=&quot;https://s.w.org/images/core/emoji/16.0.1/72x72/1f4e6.png&quot; alt=&quot;????&quot; class=&quot;wp-smiley&quot; style=&quot;height: 1em; max-height: 1em;&quot;&gt; Custom CRDs (Traefik, Kong&amp;hellip;)&lt;/h3&gt;



&lt;p&gt;Advantages:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;More structured and powerful&lt;/li&gt;
	&lt;li&gt;Better validation and control&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Disadvantages:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Adds &lt;strong&gt;new non-standard objects&lt;/strong&gt;&lt;/li&gt;
	&lt;li&gt;Requires installation and management&lt;/li&gt;
	&lt;li&gt;Less interoperability&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Result?&lt;br&gt;&lt;strong&gt;Infrastructures deeply coupled to a vendor&lt;/strong&gt;, complicating migrations, audits, and automation.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;The complexity for development teams&lt;/h2&gt;



&lt;p&gt;The design of Ingress implies two very different responsibilities:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;&lt;strong&gt;Platform:&lt;/strong&gt; defines IngressClass&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Application:&lt;/strong&gt; defines Ingress&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;But the reality is that the developer ends up making decisions that should be the responsibility of the platform area:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Certificates&lt;/li&gt;
	&lt;li&gt;Security policies&lt;/li&gt;
	&lt;li&gt;Rewrite rules&lt;/li&gt;
	&lt;li&gt;CORS&lt;/li&gt;
	&lt;li&gt;Timeouts&lt;/li&gt;
	&lt;li&gt;Corporate naming practices&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This causes:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Inconsistent configurations&lt;/li&gt;
	&lt;li&gt;Bottlenecks in reviews&lt;/li&gt;
	&lt;li&gt;Constant dependency between teams&lt;/li&gt;
	&lt;li&gt;Lack of effective standardization&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;In large companies, where security and governance are critical, this is especially problematic.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;NGINX Ingress: the decommissioning that reignited the debate&lt;/h2&gt;



&lt;p&gt;The recent &lt;strong&gt;decommissioning of the NGINX Ingress Controller&lt;/strong&gt; has highlighted the fragility of the ecosystem:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Thousands of clusters depend on it&lt;/li&gt;
	&lt;li&gt;Multiple projects use its annotations&lt;/li&gt;
	&lt;li&gt;Migrating involves &lt;strong&gt;rewriting entire configurations&lt;/strong&gt;&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;This has reignited the conversation about the need for &lt;strong&gt;a real standard&lt;/strong&gt;&amp;hellip; and there appears Gateway API.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Gateway API: a promising alternative (but not perfect)&lt;/h2&gt;



&lt;p&gt;Gateway API was born to solve many of the limitations of Ingress:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Clear separation of responsibilities (infrastructure vs application)&lt;/li&gt;
	&lt;li&gt;Standardized extensibility&lt;/li&gt;
	&lt;li&gt;More types of routes (HTTPRoute, TCPRoute&amp;hellip;)&lt;/li&gt;
	&lt;li&gt;Greater expressiveness without relying on proprietary annotations&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;But it also brings challenges:&lt;/p&gt;



&lt;ul class=&quot;wp-block-list&quot;&gt;
	&lt;li&gt;Requires gradual adoption&lt;/li&gt;
	&lt;li&gt;Not all vendors implement the same&lt;/li&gt;
	&lt;li&gt;Migration is not trivial&lt;br&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Even so, it is shaping up to be the future of traffic management in Kubernetes.&lt;/p&gt;



&lt;h2 class=&quot;wp-block-heading&quot;&gt;Conclusion&lt;/h2&gt;



&lt;p&gt;Ingresses have been fundamental to the success of Kubernetes, but their own simplicity has led them to become a bottleneck. The lack of interoperability, differences between vendors, and complex governance in enterprise environments make it clear that it is time to adopt more mature models.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Gateway API is not perfect, but it moves in the right direction.&lt;/strong&gt;&lt;br&gt;Organizations that want future stability should start planning their transition.&lt;/p&gt;



&lt;p&gt;&lt;img src=&quot;https://s.w.org/images/core/emoji/16.0.1/72x72/1f4da.png&quot; alt=&quot;????&quot; class=&quot;wp-smiley&quot; style=&quot;height: 1em; max-height: 1em;&quot;&gt; &lt;strong&gt;Want to dive deeper into Kubernetes?&lt;/strong&gt; This article is part of our &lt;a rel=&quot;nofollow&quot; href=&quot;https://alexandre-vazquez.com/kubernetes-architecture-patterns/&quot;&gt;comprehensive Kubernetes Architecture Patterns guide&lt;/a&gt;, where you&amp;rsquo;ll find all fundamental and advanced concepts explained step by step.&lt;/p&gt;

</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/11559/resolving-kubernetes-ingress-issues-limitations-and-gateway-insights</guid>
<pubDate>Fri, 13 Feb 2026 03:54:50 +0000</pubDate>
</item>
</channel>
</rss>