MYHOSPITALNOW.COM

Find Trusted Doctors & Hospitals

Discover healthcare providers, compare hospitals, and explore treatment options worldwide.

Free Treatment Quotes • Doctor Search • Hospital Comparison

Find Healthcare Options

www.myhospitalnow.com

Kubernetes Ingress Controller using Path-Based Routing: Complete Hands-On Lab with Calico VXLAN

Scope: Self-managed Kubernetes on two AWS EC2 Ubuntu nodes; install the community ingress-nginx v1.15.1 bare-metal/NodePort manifest, repair cross-node Calico networking, deploy two NGINX versions behind ClusterIP Services, configure path-based routing with URL rewriting, and validate each layer.

Production warning (November 2026): The community kubernetes/ingress-nginx project was retired in March 2026 and no longer receives security fixes. The exact v1.15.1 manifest below reproduces this existing training/lab cluster only; do not deploy it as a new production ingress solution. For new production deployments, evaluate a maintained Gateway API implementation or actively supported Ingress controller. See Kubernetes retirement announcement.

Learning objectives

By the end, you will know how to:

  1. Install the ingress-nginx controller and understand its NodePort and admission-webhook Services.
  2. Identify a Calico networking failure using routes, Pod IPs, and webhook timeouts.
  3. Enable VXLAN on an existing Calico IPPoolย when that is appropriate for your cluster.
  4. Deployย app1ย andย app2ย usingย scmgalaxy/nginx-devopsschoolv1ย andย scmgalaxy/nginx-devopsschoolv2.
  5. Routeย /app1ย andย /app2ย through aย single Ingressย with NGINX-specific path rewriting.
  6. Validate Kubernetes resources, networking, HTTP responses, and cleanup.

Lab environment and architecture

These addresses and ports are from the example lab. Replace them with values from your own cluster.

ComponentLab valuePurpose
Kubernetesv1.35.10Self-managed cluster
Control-plane node172.31.10.122Run kubectl and host networking tests
Worker node172.31.13.14Runs the Ingress controller in this lab
CNICalicoPod networking
Calico Pod CIDR192.168.0.0/16Pod address pool
Ingress controlleringress-nginx v1.15.1Lab controller, now unmaintained
Ingress Controller ServiceNodePortHTTP/HTTPS external entry point
Lab HTTP NodePort31960Discover dynamically; value may differ
Admission Service IP10.101.95.207:443Kubernetes API server -> webhook
Admission Pod IP192.168.131.65:8443Example worker Pod endpoint
App1scmgalaxy/nginx-devopsschoolv1First demo page
App2scmgalaxy/nginx-devopsschoolv2Second demo page
                         Browser / curl
                               |
                 http://WORKER_IP:HTTP_NODEPORT
                               |
                               v
                   ingress-nginx controller
                        (NodePort Service)
                               |
                   Ingress: path-based-ingress
                        /             \
             /app1(/|$)(.*)         /app2(/|$)(.*)
                rewrite to /          rewrite to /
                       |                 |
                       v                 v
                 app1-service       app2-service
                  ClusterIP          ClusterIP
                       |                 |
                       v                 v
                   app1 Pod           app2 Pod
                 NGINX image v1     NGINX image v2

   Kubernetes API server -> admission ClusterIP:443 -> controller Pod:8443
   Inter-node Pod packets -> Calico VXLAN over UDP 4789 (in this lab)

Why NodePort? This is a manually created Kubernetes cluster on EC2, not an EKS cluster automatically provisioning an AWS Load Balancer. The bare-metal ingress-nginx manifest exposes a NodePort Service. ClusterIP application Services remain private. EC2 private IPs are accessible only from networks that can reach the VPC; public Internet access also requires suitable networking and narrowly scoped security group rules.


Prerequisites

  • An existing, working Kubernetes cluster with theย kubectlย context set to the correct cluster.
  • Cluster-admin permissions for installing the controller and changing Calico resources.
  • Two schedulable nodes (the demo can run on one, but cross-node networking troubleshooting requires at least two).
  • Calico already installed if using the Calico-specific repair steps;ย this tutorial does not install Calico from scratch.
  • EC2 security groups and NACLs allowing the required node-to-node traffic (seeย Phase 2).
  • curl,ย iproute2, and optionallyย tcpdumpย on the control-plane host.
kubectl config current-context
kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide
kubectl get ingressclasses
Code language: JavaScript (javascript)

For clean file organization:

mkdir -p ~/ingress-path-lab
cd ~/ingress-path-lab

Phase 1 โ€” Install the ingress-nginx controller

1.1 Apply the exact lab manifest

Lab-only / legacy installation. This is the version used in the original demonstration.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml
Code language: JavaScript (javascript)

This creates resources including the ingress-nginx namespace, controller Deployment, Services, IngressClass, admission webhook configuration, certificate Jobs and RBAC.

1.2 Verify controller and webhook are ready

kubectl rollout status deployment/ingress-nginx-controller \
  -n ingress-nginx --timeout=180s

kubectl get pods -n ingress-nginx -o wide
kubectl get svc -n ingress-nginx
kubectl get ingressclass
kubectl get validatingwebhookconfigurations
kubectl get jobs -n ingress-nginx
Code language: JavaScript (javascript)

Expected resource names (actual NodePort numbers vary):

ingress-nginx-controller              NodePort     ...    80:<HTTP_NODEPORT>/TCP,443:<HTTPS_NODEPORT>/TCP
ingress-nginx-controller-admission    ClusterIP    ...    443/TCP

IngressClass: nginx  ->  k8s.io/ingress-nginx
Code language: HTML, XML (xml)

The admission webhook listens inside the controller Pod on TCP 8443; the admission Service exposes it at TCP 443. These are not the public application ports.

1.3 Discover the NodePort instead of hardcoding it

HTTP_NODEPORT=$(kubectl get svc ingress-nginx-controller -n ingress-nginx \
  -o jsonpath='{.spec.ports[?(@.name=="http")].nodePort}')

HTTPS_NODEPORT=$(kubectl get svc ingress-nginx-controller -n ingress-nginx \
  -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')

kubectl get svc ingress-nginx-controller -n ingress-nginx
printf 'HTTP NodePort: %s\nHTTPS NodePort: %s\n' "$HTTP_NODEPORT" "$HTTPS_NODEPORT"
Code language: PHP (php)

In this example, HTTP was 31960 and HTTPS was 30251.


Phase 2 โ€” Diagnose and fix the Calico cross-node routing issue

Only apply this change if you have the same problem. A healthy Calico cluster can use VXLAN, IP-in-IP, or unencapsulated routing with correctly configured BGP/underlay routes. ipipMode: Never and vxlanMode: Never are not inherently invalid; the failure in this lab was that there was no functional route to the other node’s Pod CIDR.

2.1 The observed error

Attempting to create ing.yaml originally failed with:

Error from server (InternalError):
failed calling webhook "validate.nginx.ingress.kubernetes.io":
Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s":
context deadline exceeded
Code language: JavaScript (javascript)

The controller Pod was 1/1 Running, the admission Service and EndpointSlice existed, and the controller logged Starting validation webhook address=":8443".

Interpretation: A Running Pod and populated Service endpoints are not proof of network reachability from the API server to the webhook.

2.2 Inspect admission Service and routes

kubectl get svc ingress-nginx-controller-admission -n ingress-nginx -o wide

kubectl get endpointslices -n ingress-nginx \
  -l kubernetes.io/service-name=ingress-nginx-controller-admission

kubectl get pods -n ingress-nginx -o wide

kubectl get validatingwebhookconfiguration ingress-nginx-admission -o yaml
Code language: JavaScript (javascript)

Find the webhook Pod IP from the EndpointSlice/Pod output. In the lab it was 192.168.131.65.

On the control-plane node:

ip route get 192.168.131.65

curl --noproxy '*' -kv --connect-timeout 5 \
  https://192.168.131.65:8443/

curl --noproxy '*' -kv --connect-timeout 5 \
  https://10.101.95.207:443/
Code language: PHP (php)

Before the fix, both curl commands timed out, and routing looked like:

192.168.131.65 via 172.31.0.1 dev enX0 src 172.31.10.122
Code language: CSS (css)

That was the AWS VPC gateway, not an appropriate configured overlay route to the remote Pod. This is a strong sign of missing remote Pod routing in this particular cluster, not a generic rule that any route through a gateway is wrong.

2.3 Check the existing Calico IPPool

kubectl get ippools.crd.projectcalico.org -o yaml

kubectl get pods -n kube-system -l k8s-app=calico-node -o wide

ip -d link show vxlan.calico || true
ip -d link show tunl0 || true
Code language: JavaScript (javascript)

Original lab IPPool:

metadata:
  name: default-ipv4-ippool
spec:
  cidr: 192.168.0.0/16
  ipipMode: Never
  vxlanMode: Never
  natOutgoing: true
Code language: JavaScript (javascript)

No functioning cross-node Pod route was present. The vxlan.calico interface did not exist, and tunl0 was down.

2.4 Check AWS network requirements before making the change

For Calico VXLAN, allow UDP 4789 in both directions between the EC2 nodes (for example, using an appropriate EC2 security-group-to-security-group rule, or narrowly scoped private node IP rules). Check custom network ACLs and host firewalls as well.

Calico transportNode-to-node traffic
VXLANUDP 4789
IP-in-IPIP protocol number 4
BGPTCP 179
Unencapsulated/direct routingPod CIDR reachability provided by the underlay and firewall rules

Do not open UDP 4789 to the public Internet. Allow client NodePort HTTP access only from trusted IP ranges for this lab.

Operator-managed Calico: If you installed Calico through the Tigera operator, check the Installation resource. Operator-managed pools should be changed via Installation.spec.calicoNetwork.ipPools, not patched directly; the operator can revert direct IPPool edits. The direct IPPool command below applies to this lab’s manifest-managed pool.

2.5 Back up and enable VXLAN Always (manifest-managed Calico)

This changes network dataplane behavior and can briefly interrupt live connections. Back up your configuration and use a maintenance window on shared clusters.

<em># Backup</em>
kubectl get ippools.crd.projectcalico.org \
  default-ipv4-ippool -o yaml > calico-ippool-backup.yaml

<em># Verify the pool name and current mode before modifying it</em>
kubectl get ippools.crd.projectcalico.org \
  default-ipv4-ippool \
  -o jsonpath='{.spec.cidr}{"  IPIP="}{.spec.ipipMode}{"  VXLAN="}{.spec.vxlanMode}{"\n"}'

<em># Enable overlay routing on the existing IPPool</em>
kubectl patch ippools.crd.projectcalico.org default-ipv4-ippool \
  --type=merge \
  -p '{"spec":{"ipipMode":"Never","vxlanMode":"Always"}}'
Code language: PHP (php)

Why Always, not CrossSubnet? The EC2 nodes are in the same VPC subnet (172.31.0.0/20). CrossSubnet may skip encapsulation between them and therefore would not solve the missing Pod-CIDR underlay routing in this lab. Always encapsulates cross-node traffic even when the nodes share a subnet.

For Tigera operator-managed Calico, instead inspect:

kubectl get installations.operator.tigera.io default -o yaml
Code language: CSS (css)

Use the operator’s documented encapsulation: VXLAN setting for the appropriate IPPool definition in that Installation resource. Do not apply the direct patch if the operator manages the pool.

2.6 Verify VXLAN and the new route

kubectl get ippools.crd.projectcalico.org default-ipv4-ippool -o yaml

ip -d link show vxlan.calico

ip route get 192.168.131.65

kubectl get pods -n kube-system -l k8s-app=calico-node -o wide
Code language: JavaScript (javascript)

Observed after the fix in this lab:

vxlan.calico: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1410
    vxlan id 4096 local 172.31.10.122 dev enX0 dstport 4789

192.168.131.65 via 192.168.131.80 dev vxlan.calico src 192.168.90.202
Code language: CSS (css)

Important: The interface and route prove that VXLAN is configured; they do not alone prove packets cross between EC2 hosts. Retest both webhook endpoints:

curl --noproxy '*' -kv --connect-timeout 5 \
  https://192.168.131.65:8443/

curl --noproxy '*' -kv --connect-timeout 5 \
  https://10.101.95.207:443/
Code language: JavaScript (javascript)

Any genuine HTTP response (including an expected 404/400 for /) demonstrates reachability. The bare curl to / is not a functional admission-validation test; it simply tests connection and TLS. The definitive admission check is that the subsequent Ingress creation succeeds.

If timeouts continue: check both EC2 security groups, return traffic/NACLs and host firewalls, and capture traffic while making a request:

sudo tcpdump -ni enX0 udp port 4789
<em># In a second terminal, repeat curl to the Pod IP and Service IP.</em>
Code language: PHP (php)

If needed, review Calico logs:

kubectl logs -n kube-system -l k8s-app=calico-node \
  --all-containers=false --tail=100
Code language: JavaScript (javascript)

MTU note from this lab

Calico also logged Failed to auto-detect host MTU - no interfaces matched the MTU interface pattern. The EC2 interface was named enX0; VXLAN later showed an MTU of 1410.

This is a separate item to review if you observe packet loss or large-request failures. Check the underlay interface MTU, tunnel MTU and Calico’s configured detection settings before making any changes. Do not blindly assign a new MTU to every cluster.

ip link show enX0
ip -d link show vxlan.calico
kubectl get felixconfigurations.crd.projectcalico.org -o yaml
kubectl get cm calico-config -n kube-system -o yaml 2>/dev/null || true
Code language: JavaScript (javascript)

Phase 3 โ€” Deploy two application Deployments and ClusterIP Services

3.1 Create apps.yaml

Use different Docker images so that you can recognize which backend answered each request.

cat > apps.yaml <<'YAML'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app1
  template:
    metadata:
      labels:
        app: app1
    spec:
      containers:
        - name: nginx
          image: scmgalaxy/nginx-devopsschoolv1
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: app1-service
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: app1
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app2
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
        - name: nginx
          image: scmgalaxy/nginx-devopsschoolv2
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: app2-service
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: app2
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
YAML
Code language: JavaScript (javascript)

3.2 Deploy and verify

kubectl apply -f apps.yaml

kubectl rollout status deployment/app1 --timeout=120s
kubectl rollout status deployment/app2 --timeout=120s

kubectl get deployments app1 app2
kubectl get pods -l 'app in (app1,app2)' -o wide
kubectl get svc app1-service app2-service

kubectl get endpointslices -l kubernetes.io/service-name=app1-service
kubectl get endpointslices -l kubernetes.io/service-name=app2-service
Code language: JavaScript (javascript)

Example ClusterIP addresses previously assigned in the lab:

app1-service   ClusterIP   10.102.53.234    80/TCP
app2-service   ClusterIP   10.103.156.112   80/TCP

They may change if Services are recreated. The Services must have ready Pod endpoints for requests to succeed.

3.3 Validate each Service before introducing Ingress

You can curl the ClusterIP from a cluster node if your node-to-Service networking is working:

curl -i --noproxy '*' http://10.102.53.234/
curl -i --noproxy '*' http://10.103.156.112/
Code language: JavaScript (javascript)

Or discover the current Service IPs automatically:

APP1_IP=$(kubectl get svc app1-service -o jsonpath='{.spec.clusterIP}')
APP2_IP=$(kubectl get svc app2-service -o jsonpath='{.spec.clusterIP}')

curl -i --noproxy '*' "http://${APP1_IP}/"
curl -i --noproxy '*' "http://${APP2_IP}/"
Code language: PHP (php)

Expected: Both root pages return HTTP 200 if the images serve /. App1 and App2 should show their respective version-specific content.


Phase 4 โ€” Configure path-based Ingress with URL rewriting

4.1 Understand why simple Prefix rules produced 404

The first Ingress used:

- path: /app1
  pathType: Prefix
  backend:
    service:
      name: app1-service
      port:
        number: 80

A Kubernetes Prefix match selects the backend Service but does not remove the prefix. The backend NGINX received /app1 instead of / and returned:

HTTP/1.1 404 Not Found
nginx/1.15.10

In this lab the controller reported NGINX 1.27.1, while the 404 page identified 1.15.10, which was a useful clue that the error came from the backend application, not the controller’s default backend. The specific version string can vary.

4.2 Create the final ing.yaml

This Ingress is controller-specific: the nginx.ingress.kubernetes.io/* annotations and regex capture rewrite depend on community ingress-nginx. Other ingress controllers may require different configuration.

cat > ing.yaml <<'YAML'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-based-ingress
  namespace: default
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
    - http:
        paths:
          - path: /app1(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: app1-service
                port:
                  number: 80
          - path: /app2(/|$)(.*)
            pathType: ImplementationSpecific
            backend:
              service:
                name: app2-service
                port:
                  number: 80
YAML

Why ImplementationSpecific? Kubernetes native path types are Exact, Prefix, and ImplementationSpecific. The NGINX-specific regular expression must be handled by the controller, rather than interpreted as a Kubernetes-native Prefix rule.

Why /$2? In each expression, (/|$) is capture group $1, and (.*) is capture group $2. Ingress-nginx rewrites the forwarded URL to / plus the content of $2.

Incoming pathSelected ServicePath the Pod receives
/app1app1-service:80/
/app1/app1-service:80/
/app1/index.htmlapp1-service:80/index.html
/app2app2-service:80/
/app2/healthapp2-service:80/health
/app3No matching application ruleController default behavior (typically 404)

4.3 Apply and verify the Ingress

kubectl apply --dry-run=client -f ing.yaml
kubectl apply -f ing.yaml

kubectl get ingress
kubectl describe ingress path-based-ingress
kubectl get ingress path-based-ingress -o yaml
Code language: JavaScript (javascript)

You should see IngressClass nginx, paths for both Services, and the two rewrite annotations.

If kubectl apply fails with an admission-webhook timeout, return to Phase 2. Do not delete the webhook as a normal fix. Admission validation is useful and should stay enabled.


Phase 5 โ€” End-to-end validation

5.1 Confirm networking, backend health, and controller

kubectl get nodes -o wide
kubectl get pods -n ingress-nginx -o wide
kubectl get pods -l 'app in (app1,app2)' -o wide
kubectl get svc app1-service app2-service
kubectl get ingress path-based-ingress
kubectl get ingressclass nginx
Code language: JavaScript (javascript)

5.2 Discover NodePort and worker node address

HTTP_NODEPORT=$(kubectl get svc ingress-nginx-controller -n ingress-nginx \
  -o jsonpath='{.spec.ports[?(@.name=="http")].nodePort}')

WORKER_IP=172.31.13.14  <em># Replace with a reachable node private IP</em>

printf 'Testing http://%s:%s\n' "$WORKER_IP" "$HTTP_NODEPORT"
Code language: PHP (php)

Example: http://172.31.13.14:31960.

5.3 Test both paths

curl -i --noproxy '*' "http://${WORKER_IP}:${HTTP_NODEPORT}/app1"

curl -i --noproxy '*' "http://${WORKER_IP}:${HTTP_NODEPORT}/app2"
Code language: JavaScript (javascript)

Expected: HTTP/1.1 200 OK and the body of NGINX demo v1 at /app1 and demo v2 at /app2 (assuming both images serve their root pages correctly).

Additional checks:

<em># Rewritten to /index.html by ingress-nginx</em>
curl -i --noproxy '*' "http://${WORKER_IP}:${HTTP_NODEPORT}/app1/index.html"
curl -i --noproxy '*' "http://${WORKER_IP}:${HTTP_NODEPORT}/app2/index.html"

<em># Negative test โ€” no matching rule</em>
curl -i --noproxy '*' "http://${WORKER_IP}:${HTTP_NODEPORT}/does-not-exist"
Code language: PHP (php)

5.4 Check from a local laptop if EC2 NodePort is not directly reachable

The lab uses private EC2 IP addresses. If your laptop is not connected to the VPC, use SSH/VPN/SSM to access a node, or run this temporary port-forward from a machine where kubectl can reach the cluster:

kubectl -n ingress-nginx port-forward \
  svc/ingress-nginx-controller 8080:80

From another terminal on that same machine:

curl -i http://127.0.0.1:8080/app1
curl -i http://127.0.0.1:8080/app2
Code language: JavaScript (javascript)

Port forwarding does not require exposing the NodePort to the public Internet.

5.5 Confirm which backend handled the request

kubectl get pods -l app=app1 -o wide
kubectl get pods -l app=app2 -o wide

kubectl logs deployment/app1 --tail=30
kubectl logs deployment/app2 --tail=30

kubectl logs -n ingress-nginx \
  deployment/ingress-nginx-controller --tail=60
Code language: JavaScript (javascript)

Compare page content and each application’s access logs after hitting /app1 and /app2. Different Docker images make routing demonstrations much clearer.


Phase 6 โ€” Troubleshooting reference

SymptomLikely causeChecks / fix
failed calling webhook ... context deadline exceededAPI server cannot reach admission ServiceVerify EndpointSlice, Pod route, CNI, kube-proxy, security groups, TCP 8443 / Service 443
Pod IP and ClusterIP both time out from control planeCross-node Pod networking/underlay or firewall issueip route get POD_IP; verify Calico overlay and UDP 4789
vxlan.calico missing after patchCalico not configured/reconciled for VXLANIPPool mode, operator ownership, calico-node logs and DaemonSet settings
vxlan.calico is UP but curl still times outPackets blocked or reverse path brokenEC2 SG/NACL, host firewall, tcpdump -ni enX0 udp port 4789
Ingress admission succeeds but /app1 is 404 from backend NGINXPrefix path was not removedUse regex pattern and rewrite annotation in Phase 4
Ingress returns 404 for /app3No matching ruleExpected unless another/default backend is configured
HTTP 503 from IngressService has no ready backends, wrong name/port, or backend unreachablekubectl get endpointslices; Pod readiness; service selectors
NodePort request cannot connectWrong NodePort, no reachable node IP, or blocked ingress trafficQuery actual Service NodePort; test from node/VPC or port-forward
502/504 or intermittent issuesBackend/process failure, CNI problems, traffic policiesLogs, Pod health, NetworkPolicies, MTU and packet captures
LoadBalancer Service EXTERNAL-IP is <pending>No cloud LB integration in self-managed clusterFor this lab use the ingress-nginx NodePort Service

Deep-dive diagnostic commands

<em># Ingress details</em>
kubectl describe ingress path-based-ingress
kubectl get ingress path-based-ingress -o yaml

<em># Controller and admission endpoints</em>
kubectl get svc -n ingress-nginx
kubectl get endpointslices -n ingress-nginx \
  -l kubernetes.io/service-name=ingress-nginx-controller-admission
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller --tail=100

<em># Networking and routes</em>
kubectl get ippools.crd.projectcalico.org -o yaml
kubectl get pods -n kube-system -o wide
ip route
ip -d link show vxlan.calico

<em># Backend Pods and endpoints</em>
kubectl describe deployment app1
kubectl describe deployment app2
kubectl get endpointslices -l kubernetes.io/service-name=app1-service
kubectl get endpointslices -l kubernetes.io/service-name=app2-service
Code language: HTML, XML (xml)

Avoid the unsafe shortcut: Changing the admission webhook to failurePolicy: Ignore is not a networking fix. It can mask errors. If temporarily used for an isolated lab diagnosis, restore the original validation policy afterward and verify that admission calls succeed normally.


Phase 7 โ€” Optional cleanup and rollback

Delete only the demo Ingress and applications

kubectl delete -f ing.yaml
kubectl delete -f apps.yaml
Code language: JavaScript (javascript)

Remove the lab controller only if it is not used elsewhere

kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml
Code language: JavaScript (javascript)

Caution: This can disrupt other Ingress resources that depend on the same controller. Do not remove it on a shared cluster without checking usage.

Roll back Calico encapsulation only when you have a working alternative route

Do not blindly revert VXLAN to Never: that would recreate the original cross-node failure unless you first set up working BGP/unencapsulated routing or an alternative overlay. Keep the backup file for review; do not apply a stale backup automatically to a changed production cluster.


Final success checklist

  • [ ]ย kubectl get nodesย shows both nodesย Ready.
  • [ ]ย ingress-nginx-controllerย is ready and itsย NodePortย Service exists.
  • [ ] Calico IPPool encapsulation is intentional, with required node-to-node transport allowed.
  • [ ]ย ip route get <REMOTE_POD_IP>ย shows a usable route to the remote Pod.
  • [ ] Admission webhook Service/Pod TCP connectivity works from the control plane.
  • [ ] Bothย app1ย andย app2ย Deployments are ready.
  • [ ] Both ClusterIP Services have ready Pod endpoints.
  • [ ]ย path-based-ingressย was accepted by Kubernetes admission validation.
  • [ ]ย GET /app1ย returns application v1 content.
  • [ ]ย GET /app2ย returns application v2 content.
  • [ ]ย GET /app1/index.htmlย andย /app2/index.htmlย work with URL rewriting.

Command summary

<em># 1. Controller (legacy lab-only)</em>
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml

<em># 2. Calico: only if the manifest-managed IPPool lacks working cross-node routing</em>
kubectl patch ippools.crd.projectcalico.org default-ipv4-ippool \
  --type=merge -p '{"spec":{"ipipMode":"Never","vxlanMode":"Always"}}'

<em># 3. Applications</em>
kubectl apply -f apps.yaml

<em># 4. Ingress</em>
kubectl apply -f ing.yaml

<em># 5. Verify</em>
kubectl get pods,deploy,svc,ingress
kubectl get svc -n ingress-nginx
curl -i http://172.31.13.14:31960/app1
curl -i http://172.31.13.14:31960/app2
Code language: PHP (php)

The two YAML manifests are supplied in full in Phases 3 and 4. The 31960 port and 172.31.13.14 address in the summary are the example lab valuesโ€”discover your actual NodePort and node address before testing.

References

  1. Kubernetes โ€” Ingress concepts and path types
  2. ingress-nginx โ€” Installation on bare-metal clusters (archived documentation)
  3. ingress-nginx โ€” Rewrite example and capture groups
  4. Calico โ€” Overlay networking with VXLAN and IP-in-IP
  5. Calico โ€” Network port requirements
  6. Calico โ€” IPPool reference
  7. Kubernetes โ€” Ingress NGINX retirement announcement
  8. Kubernetes โ€” January 2026 retirement/security statement

Lab snapshot and troubleshooting history: November 12, 2026. Addresses, resource names, versions, and port numbers reflect the example cluster unless explicitly described as dynamic.

Find Trusted Cardiac Hospitals

Compare heart hospitals by city and services โ€” all in one place.

Explore Hospitals
I'm Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms. I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.

Related Posts

Deploying Nexus Certificate Manager (CM) and Protocol Gateway (PGWY) on Kubernetes

Step-by-step tutorial, written from an actual deployment of Certificate Manager 8.15.1-1 (CF server image 8.15.1-1, PGW image 8.15.1-1) on a 2-node kubeadm cluster. Status of this document (2026-11-12) Steps 1-8 were executed…

Read More

Kubernetes storageclassย using OpenEBS Local PV HostPath โ€” Complete Step-by-Step Kubernetes Tutorial

Dynamic storage provisioner: openebs.io/localLab platform: Self-managed Kubernetes v1.35.10 on Ubuntu Linux (kubeadm or other manual installation)OpenEBS release referenced: OpenEBS 4.6.2 (HostPath engine v4.6.0), current as checked on November 12, 2026Lab…

Read More

Kubernetes storageclass using Rancher Local Path Provisioner: Complete Step-by-Step Kubernetes Tutorial

Goal: Automatically provision hostPath-backed PersistentVolumes (PVs) for a 3-replica StatefulSet on a self-managed Ubuntu Kubernetes cluster, without hand-writing a PV or a PVC manifest. Lab setting Value Kubernetes v1.35.10 (self-managed / kubeadm-style installation)…

Read More

Understanding the Hardware Requirements of Modern LLMs

Ask ten engineers how much hardware a large language model needs, and you will get ten answers, most of them starting with “it depends.” That answer is…

Read More

Best Remote SDR Outsourcing Companies 2026

Quick Answer The best remote SDR outsourcing companies in 2026 include Remote Growth Partners, Virtustant, Hire Overseas, CIENCE, SalesRoads, Belkins, SalesHive, Leadium, Martal Group, and Callbox. Dedicated…

Read More

Top Legacy Software Modernization Companies in Europe: A Full Guide for 2027

The UK government’s State of Digital Government Review put legacy systems at 28% of central government department estates in 2024, up from 26% a year earlier. Inside…

Read More
Subscribe
Notify of
guest
0 Comments
Newest
Oldest Most Voted
0
Would love your thoughts, please comment.x
()
x