Home › CKA › Topics › Practice tasks › Gateway API
CKA · Services & Networking

Migrate an Ingress to the Gateway API

Migrate the existing Ingress web in namespace web-app to the new Gateway API. A GatewayClass named nginx is already installed.

Check live lab access → All CKA tasks

Live lab access

This task description is public. Its live lab requires full access; six other CKA labs are available in Free Practice with AI Lab Coach.

Create an account to continue →

Explore the six free labs with AI Lab Coach →

Read the brief on any device. Use a desktop or laptop for the terminal lab. Nothing starts until you choose Start.

The task

Migrate the existing Ingress web in namespace web-app to the new Gateway API.

A GatewayClass named nginx is already installed. Use API version v1 (the older v1beta1 is also accepted on this environment).

Task 1: Create a Gateway named web-gateway with:

Task 2: Create an HTTPRoute named web-route with:

Original Prepium guide · Written for Kubernetes v1.35 and checked against the official documentation on 6 October 2026. Use a disposable practice cluster. Gateway API CRDs are not built into Kubernetes; you need them plus a controller that provides a GatewayClass (kubectl get gatewayclass must list one).

Gateway API in three objects: class, Gateway listeners and HTTPRoute, with TLS termination

An Ingress packs the controller choice, hostnames, certificates and paths into one object. Gateway API splits them by role: a GatewayClass names the controller, a Gateway declares listeners (port, protocol, hostname, certificates), and an HTTPRoute attaches to a listener and says which paths go to which Services. All three are served as gateway.networking.k8s.io/v1. Translating an Ingress is mostly a matter of moving each field to the right object.

Ingress field                  Gateway API home
spec.ingressClassName      ──→ Gateway spec.gatewayClassName
spec.tls[].secretName      ──→ Gateway listener tls.certificateRefs
spec.rules[].host          ──→ listener hostname + HTTPRoute hostnames
paths[].path / pathType    ──→ HTTPRoute rules[].matches[].path
backend.service name/port  ──→ HTTPRoute rules[].backendRefs

1. Prepare backends and a certificate

kubectl create namespace prepium-tutorial
kubectl -n prepium-tutorial create deployment docs-web --image=nginx:1.27-alpine
kubectl -n prepium-tutorial create deployment status-web --image=nginx:1.27-alpine
kubectl -n prepium-tutorial expose deployment docs-web --port=8081 --target-port=80
kubectl -n prepium-tutorial expose deployment status-web --port=8082 --target-port=80
openssl req -x509 -newkey rsa:2048 -noenc -days 7 -keyout pages.key -out pages.crt \
  -subj "/CN=pages.prepium.test" -addext "subjectAltName=DNS:pages.prepium.test"
kubectl -n prepium-tutorial create secret tls pages-cert --cert=pages.crt --key=pages.key

kubectl create secret tls stores the pair under the keys tls.crt and tls.key (on OpenSSL older than 3.0, use -nodes instead of -noenc). Keep the Secret in the Gateway's namespace; a reference to another namespace is rejected unless a ReferenceGrant there allows it.

2. Declare the Gateway and the route

Save this as gateway-edge.yaml. It defines two listeners on the same hostname and one HTTPRoute that attaches only to the HTTPS listener through sectionName.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: edge
  namespace: prepium-tutorial
spec:
  gatewayClassName: GATEWAY_CLASS      # replaced by sed with a real class name
  listeners:
  - name: plain
    protocol: HTTP
    port: 80
    hostname: pages.prepium.test
  - name: secure
    protocol: HTTPS
    port: 443
    hostname: pages.prepium.test
    tls:
      mode: Terminate                  # the default, written out for clarity
      certificateRefs:
      - kind: Secret
        name: pages-cert
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: pages
  namespace: prepium-tutorial
spec:
  parentRefs:
  - name: edge
    sectionName: secure                # attach to the HTTPS listener only
  hostnames:
  - pages.prepium.test
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /status
    backendRefs:
    - name: status-web
      port: 8082
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: docs-web
      port: 8081
GWC=$(kubectl get gatewayclass -o jsonpath='{.items[0].metadata.name}')
sed "s/GATEWAY_CLASS/$GWC/" gateway-edge.yaml | kubectl apply -f -

On an HTTPS listener tls.mode defaults to Terminate: the Gateway decrypts and the backends receive plain HTTP. In an HTTPRoute, a port on a Service backendRef is required and means the Service port, not the container port. When paths overlap, the longest matching prefix wins, so /status reaches status-web and everything else falls through to /.

3. Read status before sending traffic

kubectl -n prepium-tutorial get gateway edge
kubectl -n prepium-tutorial describe gateway edge
kubectl -n prepium-tutorial get httproute pages -o jsonpath='{range .status.parents[*].conditions[*]}{.type}={.status} {.reason}{"\n"}{end}'

The Gateway should report Accepted and Programmed, and each listener should show ResolvedRefs True; a missing Secret appears there as reason InvalidCertificateRef. Each parent entry on the route carries Accepted and ResolvedRefs; a wrong Service name or listener name shows up as a False condition long before a request fails.

4. Test with the hostname, not the IP

GW=$(kubectl -n prepium-tutorial get gateway edge -o jsonpath='{.status.addresses[0].value}')
curl -sk --resolve pages.prepium.test:443:$GW https://pages.prepium.test/
curl -sk --resolve pages.prepium.test:443:$GW https://pages.prepium.test/status

--resolve makes curl send the right SNI and Host header to the Gateway address, and -k accepts the self-signed certificate. Whether that address is reachable from your shell depends on the implementation and your network.

Common mistakes

Example checkEvidence
Gateway uses the intended classspec.gatewayClassName matches an installed GatewayClass; Accepted and Programmed are True
TLS is terminated at the listenerHTTPS listener whose certificateRefs names an existing TLS Secret in the same namespace
Route is attachedparentRefs names the Gateway; route conditions Accepted and ResolvedRefs are True
Routing works end to endHTTPS request with the right Host returns each backend for its path

Cleanup: kubectl delete namespace prepium-tutorial and rm pages.key pages.crt. References: Migrating from Ingress, TLS configuration, API specification. Next, try Ingress path routing or TLS protocol versions.

Exam
CKA
Domain
Services & Networking
Grading
Programmatic · partial credit

What this tests

Expose workloads with services and ingress, and restrict traffic with network policies. On the CKA exam, Services & Networking tasks are graded purely on what you build in the cluster - not multiple choice - so the only way to get faster is to do them on a real cluster against a clock.

Practice it for real

The public description lets you study the task before signing up. Full access includes every live lab. Six selected CKA scenarios are included in Free Practice, with five shared sessions each week. You solve the task in a real terminal, hit validate, and a programmatic checker scores exactly what you got right and wrong (with partial credit). You can open the solution while practising, then retry and use the failed checks to improve your score.

Related CKA tasks