Skip to content
CloudOps
Newsletter
All guides
AI for NGINX By James Joyner IV · · 11 min read

Configuring the NGINX Ingress Controller in Kubernetes With AI

Draft and decode NGINX Ingress manifests with AI: ingressClassName, pathType, cert-manager TLS, and annotations validated with kubectl and the rendered config.

  • #nginx
  • #ai
  • #kubernetes
  • #ingress

A teammate pinged me at 4:47 on a Friday because a new service kept returning 404s through the cluster’s load balancer, even though the pod was healthy and the Service had endpoints. The Ingress looked fine to him. It wasn’t. He’d written nginx.ingress.kubernetes.io/rewrite-target: /$1 but his path rule had no capture group, so the controller rewrote every request to a literal /$1. The pod never saw a valid path. He’d copied the annotation from a blog post that assumed a regex path he didn’t have. That bug is the whole reason I trust AI to draft Ingress manifests and explain annotations, and trust kubectl plus the rendered nginx.conf to tell me whether the draft is actually correct.

AI is genuinely good at the parts of Ingress that are tedious and error-prone: remembering which annotation prefix belongs to which controller, recalling that pathType: Prefix matches path segments and not substrings, and stubbing out a manifest with TLS wired to a cert-manager secret. It is not good at knowing what your controller actually rendered. So the loop is: prompt for a draft, read it like a code reviewer, apply it, then go look at the truth inside the controller pod.

Start With a Clear Prompt

The single biggest mistake people make is asking AI for “an Ingress” without telling it which controller they run. There are two completely different projects that both get called “the NGINX Ingress Controller,” and they do not share an annotation namespace. More on that below. Pin it down in the prompt:

Write a Kubernetes Ingress for the community ingress-nginx controller (kubernetes/ingress-nginx). Host app.example.com. Route / to Service web port 80 and /api to Service api port 8080. Use ingressClassName: nginx. Terminate TLS using a cert-manager-issued secret named app-example-tls. Add annotations to force SSL redirect and set proxy body size to 25m. Use pathType: Prefix. Explain each annotation in a comment.

That prompt does the work for you: it names the controller, fixes the class, specifies the secret instead of letting the model invent one, and asks for inline explanations so you can sanity-check the reasoning. Here’s the kind of manifest that comes back, cleaned up to what I’d actually apply:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: storefront
  annotations:
    # Redirect HTTP to HTTPS (default true once TLS is set, but be explicit)
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # Allow larger uploads; maps to client_max_body_size in nginx.conf
    nginx.ingress.kubernetes.io/proxy-body-size: "25m"
    # cert-manager watches this Ingress and fills the TLS secret
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-example-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 8080
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

Note the order: I put /api before /. With Prefix matching, the controller sorts by specificity, but I list the more specific path first anyway so the manifest reads the way the routing actually resolves. Small habit, fewer surprises.

pathType Is Where People Get Burned

pathType has three values and AI gets them right more often than humans do, but you still need to understand the call it’s making.

  • Prefix matches by URL path segments. /api matches /api and /api/v1 but not /apidocs. This is what you want 90% of the time.
  • Exact matches the path with no trailing-slash forgiveness. /health matches /health only, never /health/.
  • ImplementationSpecific hands matching to the controller. On ingress-nginx this is where regex paths and capture groups live — and it’s the only place rewrite-target with a $1 capture makes sense.

That last point is exactly the Friday-afternoon bug. If you ask AI for a path rewrite, make it show you both halves:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /svc(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: api
                port:
                  number: 8080

Here $2 refers to the second capture group (.*), so /svc/users reaches the backend as /users. The capture group in the path and the $2 in the annotation are a matched pair. If AI hands you a rewrite-target with a $ variable, the very next thing you check is whether the path actually defines that group.

Apply, Then Go Find the Truth

Drafting is half the job. Validation is the half that keeps you out of the incident channel.

kubectl apply -f web-ingress.yaml

# Did the controller accept it and assign an address?
kubectl describe ingress web -n storefront

kubectl describe shows you the parsed rules, the events, and — critically — whether the controller logged a warning about an annotation it didn’t understand. A typo’d annotation key is silently ignored, not rejected, so the describe output and controller logs are your only signal.

Then dump what the controller actually rendered. The annotations are inputs; the generated nginx.conf is the output that serves traffic. They are not the same thing.

# Find the controller pod
POD=$(kubectl get pods -n ingress-nginx \
  -l app.kubernetes.io/name=ingress-nginx -o name | head -n1)

# Dump the generated config and grep your host out of it
kubectl exec -n ingress-nginx "$POD" -- cat /etc/nginx/nginx.conf \
  | grep -A 30 "app.example.com"

# Validate the rendered config syntax inside the pod
kubectl exec -n ingress-nginx "$POD" -- nginx -t

When I run that grep, I’m looking for the concrete directives my annotations were supposed to produce: a client_max_body_size 25m; for the proxy-body-size annotation, a return 308 redirect block for ssl-redirect, and a rewrite line that matches my capture group. If an annotation didn’t translate the way I expected, this is where it shows up — in plain nginx directives I can read. AI is a great pair for this step too: paste a confusing rendered server block into it and ask “explain what this location block does and which annotation produced it.” That round-trip is faster than digging through the controller’s templating source. If you want to go deeper on hardening the rendered output, I wrote up the review side in reviewing NGINX security configuration with AI.

Rate Limiting and Other Annotation Traps

Rate limiting is a good example of “AI saves you a docs trip, but you verify the number.” On ingress-nginx:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "10"
    nginx.ingress.kubernetes.io/limit-connections: "20"
    nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"

limit-rps is requests per second per client IP, and the burst multiplier controls how much short-term spikiness the controller tolerates before it starts returning 503s. Ask AI to explain the interaction and it’ll walk you through how those map to limit_req_zone and limit_req in the rendered config — then go confirm the zone size and burst in the actual nginx.conf, because the defaults around shared-memory zones are easy to get wrong at scale.

The ingress-nginx vs F5 NGINX Distinction

This trips up AI and humans equally, so call it out explicitly in every prompt. There are two controllers:

  • Community ingress-nginx (kubernetes/ingress-nginx) uses the nginx.ingress.kubernetes.io/ annotation prefix. This is the one most clusters run, and everything above is written for it.
  • F5 NGINX Ingress Controller (nginxinc/kubernetes-ingress) uses the nginx.org/ prefix and its own VirtualServer/VirtualServerRoute CRDs. Its annotations and CRDs are not interchangeable with the community ones.

So nginx.ingress.kubernetes.io/rewrite-target and nginx.org/rewrites are different controllers’ takes on the same idea, with different syntax. If you copy a manifest from the wrong project’s docs, the annotations are silently dropped and your routing quietly does the default thing. When you prompt AI, name the repo, not just “NGINX Ingress.” I keep a couple of these prompts saved in my prompts collection so I don’t have to re-explain the distinction every time.

The Loop That Actually Works

AI drafts the manifest and explains the annotations. You read it like a reviewer, confirm the pathType and any capture groups line up, apply it, and then prove it with kubectl describe, a grep of the rendered nginx.conf, and nginx -t inside the controller pod. The model is fast at the parts your memory is bad at; the cluster is the only authority on what’s actually serving traffic. Keep both in the loop and Ingress stops being a Friday-afternoon mystery. For more in this vein, the rest of the AI for NGINX category goes deeper on configs, security, and tuning.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.