Back to Blog

CKAD Services & Networking: Services, Ingress & NetworkPolicy

A hands-on CKAD guide to Kubernetes Services, Ingress, and NetworkPolicy with kubectl commands, YAML examples, and exam-day shortcuts to expose and secure apps fast.

By Sailor Team , June 5, 2026

Services & Networking is one of the most practical domains on the Certified Kubernetes Application Developer (CKAD) exam — and one where candidates lose easy points under time pressure. The tasks are rarely conceptually hard, but they reward muscle memory: knowing exactly which Service type to reach for, how to wire a NetworkPolicy without locking yourself out, and how to expose a Deployment through Ingress in under two minutes.

This guide walks through everything in the CKAD Services & Networking domain from a practitioner’s perspective. You’ll get working YAML, the imperative kubectl shortcuts that save time on exam day, and the troubleshooting moves that turn a broken connection into a green checkmark.

What the CKAD Tests in Services & Networking

The “Services & Networking” domain accounts for roughly 20% of the CKAD exam. The official curriculum expects you to:

  • Demonstrate a basic understanding of NetworkPolicies
  • Provide and troubleshoot access to applications via Services
  • Use Ingress rules to expose applications

In plain terms: you need to make a Pod reachable, control who can talk to it, and route external HTTP traffic to it. Everything below maps directly to those three abilities.

Before going further, remember the golden rule of every CKAD networking task: a Service finds Pods by label selector, not by name. If a Service has zero endpoints, the selector almost always doesn’t match the Pod labels. Burn that into memory now and you’ll save yourself half your troubleshooting time.

Kubernetes Services: The Four Types

A Service is a stable network identity (a virtual IP and DNS name) that load-balances traffic across a dynamic set of Pods. Pods are ephemeral — they come and go with rolling updates and rescheduling — so you never connect to a Pod IP directly. You connect to the Service.

Service TypeWhat it doesTypical CKAD use
ClusterIPInternal-only virtual IP (the default)Service-to-service inside the cluster
NodePortOpens a static port (30000–32767) on every nodeQuick external access for testing
LoadBalancerProvisions an external load balancer (cloud)Production external access
ExternalNameMaps the Service to an external DNS name via CNAMEAliasing an external dependency

For the exam, you live mostly in ClusterIP and NodePort territory. LoadBalancer and ExternalName appear less often, but you should recognize them.

Creating a Service the Fast Way

Typing a Service YAML by hand wastes precious minutes. Use the imperative generators instead.

Expose an existing Deployment as ClusterIP:

kubectl expose deployment web --port=80 --target-port=8080 --name=web-svc

--port is the port the Service listens on; --target-port is the container port it forwards to. If you omit --target-port, it defaults to --port.

Create a ClusterIP Service for a Pod in one shot:

kubectl run nginx --image=nginx --port=80 --expose

Generate a NodePort Service:

kubectl expose deployment web --type=NodePort --port=80 --name=web-np

You can’t set a specific nodePort with kubectl expose directly, so if a task demands a fixed port like 30080, generate the YAML and edit it:

kubectl expose deployment web --type=NodePort --port=80 --dry-run=client -o yaml > svc.yaml

Then add nodePort: 30080 under the port entry. The full YAML looks like this:

apiVersion: v1
kind: Service
metadata:
  name: web-np
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080

Verifying a Service Actually Works

The single most useful command in this entire domain:

kubectl get endpoints web-svc

If you see Pod IPs listed, your selector matches and the Service is wired correctly. If you see <none>, the selector is wrong — compare it against the Pod labels:

kubectl get pods --show-labels
kubectl describe svc web-svc | grep -i selector

To test connectivity from inside the cluster without leaving the terminal, spin up a throwaway client:

kubectl run tmp --image=busybox --rm -it --restart=Never -- wget -qO- web-svc:80

The --rm flag deletes the Pod when you exit, keeping your namespace clean.

Service DNS: Know the Naming Convention

Kubernetes DNS gives every Service a predictable name. On the exam you’ll often need to reference a Service from another namespace, and the FQDN pattern is:

<service-name>.<namespace>.svc.cluster.local

So a Service named db in namespace data is reachable as:

  • db — from a Pod in the same namespace
  • db.data — from any namespace (short form)
  • db.data.svc.cluster.local — fully qualified

If an app in namespace web can’t reach db in namespace data, check that it’s using db.data, not just db. This is a classic exam trap.

Ingress: Routing External HTTP Traffic

A Service of type NodePort or LoadBalancer exposes a single app on a port. Ingress is smarter — it routes external HTTP(S) traffic to multiple Services based on hostname and path, all behind one entry point. Ingress requires an Ingress controller (NGINX, Traefik, etc.) to actually do the routing; the Ingress resource is just the rules.

A Practical Ingress Example

Say you have two Services — api-svc and web-svc — and you want path-based routing under app.example.com:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-svc
                port:
                  number: 80

Key fields the exam cares about:

  • pathType — almost always Prefix for CKAD tasks. Exact matches the path literally; Prefix matches /api and everything beneath it.
  • backend.service.port.number — this is the Service port, not the container port. A frequent mistake is putting the container’s targetPort here.
  • host — optional. Omit it to match all hostnames.

Generating Ingress Imperatively

Newer kubectl versions support an Ingress generator, which is a huge time-saver:

kubectl create ingress app-ingress \
  --rule="app.example.com/api*=api-svc:80" \
  --rule="app.example.com/*=web-svc:80"

The * at the end of the path sets pathType: Prefix automatically. Always verify with kubectl get ingress and kubectl describe ingress app-ingress — the describe output shows whether the backend Services resolved.

NetworkPolicy: Controlling Pod-to-Pod Traffic

By default, every Pod can talk to every other Pod in a Kubernetes cluster. A NetworkPolicy changes that by selecting Pods and declaring which ingress (incoming) and egress (outgoing) traffic is allowed. NetworkPolicies are additive and require a CNI plugin that enforces them (Calico, Cilium, etc.) — on a cluster without one, policies are silently ignored.

Three rules to internalize before you touch a single YAML:

  1. NetworkPolicies are allow-lists. Once a Pod is selected by any policy for a direction, everything not explicitly allowed in that direction is denied.
  2. A policy with an empty podSelector: {} selects every Pod in the namespace.
  3. policyTypes declares which directions the policy governs. If you specify Ingress only, egress is untouched.

Default-Deny: The Most Common Exam Task

A frequent CKAD ask is “deny all incoming traffic to Pods in namespace X.” That’s this:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: secure
spec:
  podSelector: {}
  policyTypes:
    - Ingress

The empty podSelector selects all Pods; the absence of any ingress rule means nothing is allowed in. To lock down both directions, add Egress to policyTypes.

Allowing Specific Traffic

Now the realistic version: allow Pods labeled app: frontend to reach Pods labeled app: backend on port 8080, and nothing else.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend
  namespace: secure
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

Read it top to bottom: this policy applies to backend Pods, governs ingress, and allows traffic from frontend Pods on TCP 8080.

The namespaceSelector vs podSelector Trap

This is where candidates lose points. Inside a from block, the indentation determines the logic:

# AND: from Pods labeled role=client THAT ARE IN namespaces labeled team=web
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            team: web
        podSelector:
          matchLabels:
            role: client
# OR: from any Pod in namespaces labeled team=web, OR any Pod labeled role=client
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            team: web
      - podSelector:
          matchLabels:
            role: client

The difference is a single dash. In the first, namespaceSelector and podSelector are in the same list item (logical AND). In the second, they’re separate list items (logical OR). When a task says “from Pods labeled X in namespace Y,” that’s an AND — keep them under one dash.

Don’t Forget DNS in Egress Policies

If you write a default-deny egress policy, your Pods can no longer resolve DNS, and every outbound connection breaks with a name-resolution error. You almost always need to explicitly allow egress to the cluster DNS:

egress:
  - to:
      - namespaceSelector: {}
    ports:
      - protocol: UDP
        port: 53
      - protocol: TCP
        port: 53

This is a favorite “gotcha” — a task asks you to restrict egress, your policy looks correct, but the app times out because it can’t reach kube-dns.

Troubleshooting Workflow for Exam Day

When a connectivity task fails, work through this checklist in order. It catches the overwhelming majority of issues:

  1. Endpoints first. kubectl get endpoints <svc> — no endpoints means a selector mismatch.
  2. Compare labels. kubectl get pods --show-labels against kubectl describe svc <svc>.
  3. Check ports. Is targetPort the actual container port? Is the Pod listening on it?
  4. Test internally. kubectl run tmp --image=busybox --rm -it --restart=Never -- wget -qO- <svc>:<port>.
  5. Look for NetworkPolicies. kubectl get netpol -A — a default-deny somewhere may be blocking you.
  6. Verify cross-namespace DNS. Use <svc>.<namespace> form, not just <svc>.
  7. For Ingress, confirm the backend Service exists and the controller is running.

Time-Saving Aliases and Shortcuts

The CKAD is time-constrained. Set these up in the first 60 seconds:

alias k=kubectl
export do="--dry-run=client -o yaml"
export now="--force --grace-period=0"

Then everything becomes faster:

k expose deploy web --port=80 $do > svc.yaml
k create ingress web --rule="/*=web:80" $do > ing.yaml

Also memorize the explain command for when you blank on a field:

kubectl explain networkpolicy.spec.ingress --recursive

This prints the entire schema for the ingress block — invaluable when you can’t remember whether it’s from or source.

Putting It All Together: A Realistic Scenario

A complete exam-style task might read: “In namespace shop, expose the catalog Deployment internally on port 80, route external traffic to it at shop.local/catalog, and ensure only Pods labeled tier: gateway can reach it.”

Your solution, end to end:

# 1. Internal Service
kubectl -n shop expose deployment catalog --port=80 --target-port=8080 --name=catalog

# 2. Ingress
kubectl -n shop create ingress catalog --rule="shop.local/catalog*=catalog:80"
# 3. NetworkPolicy restricting access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: catalog-allow-gateway
  namespace: shop
spec:
  podSelector:
    matchLabels:
      app: catalog
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              tier: gateway
      ports:
        - protocol: TCP
          port: 8080

Verify each piece: kubectl -n shop get endpoints catalog, kubectl -n shop describe ingress catalog, and a quick wget test from a tier: gateway Pod versus a non-gateway Pod.

How to Practice This Until It’s Automatic

Reading YAML is not the same as producing it under a ticking clock. The only reliable way to master Services & Networking is repetition in a real cluster: spin up Deployments, expose them every way, break the selectors on purpose, and watch endpoints disappear. Write a default-deny policy and feel the app go dark, then open it back up one rule at a time.

If you’d rather practice against realistic, exam-style scenarios with detailed explanations, the CKAD Certification Ready Mock Exam Bundle runs directly in your browser and includes hands-on networking labs that mirror the real exam’s question style. It’s a structured way to build the muscle memory this domain demands — each task comes with a breakdown of why the answer works, so you’re learning patterns, not just memorizing commands.

For the broader picture, pair this with our CKAD Exam Guide 2026 and CKAD study plan, and keep the CKAD kubectl cheat sheet open while you drill.

Frequently Asked Questions

Do I need to memorize NetworkPolicy YAML for the CKAD?

You don’t need to memorize it perfectly, but there’s no imperative generator for NetworkPolicies, so you’ll write them by hand. Memorize the skeleton — podSelector, policyTypes, ingress/egress with from/to and ports — and lean on kubectl explain networkpolicy.spec --recursive for the rest.

What’s the difference between port, targetPort, and nodePort?

port is the port the Service exposes inside the cluster. targetPort is the container port traffic is forwarded to. nodePort is the external port (30000–32767) opened on every node when the Service type is NodePort. Mixing these up is the most common Service mistake.

Why does my Service have no endpoints?

Almost always a label selector mismatch. The Service’s spec.selector must exactly match the Pod labels. Run kubectl get pods --show-labels and kubectl describe svc <name> and compare them character by character.

Will NetworkPolicies work on any cluster?

Only on clusters with a CNI plugin that enforces them, such as Calico or Cilium. On a cluster without policy enforcement, NetworkPolicy resources are accepted but have no effect. The CKAD exam environment supports them, so write them as if they’re enforced.

Is Ingress or NodePort better for exposing apps?

For real production HTTP routing, Ingress is better — it handles multiple hosts and paths behind one entry point with TLS. NodePort is fine for quick testing or non-HTTP traffic. On the exam, read the task carefully: if it mentions hostnames or paths, use Ingress; if it just says “expose on a node port,” use NodePort.

How much of the CKAD is networking?

The Services & Networking domain is about 20% of the exam. Combined with how often networking touches other tasks (you frequently expose the apps you build), it’s worth investing real practice time here.

Key Takeaways

  • A Service finds Pods by label selector — check kubectl get endpoints first when connectivity breaks.
  • Know the four Service types and the port / targetPort / nodePort distinction cold.
  • Use kubectl expose and kubectl create ingress to generate resources fast, then edit the YAML.
  • Ingress routes external HTTP by host and path; the backend port is the Service port.
  • NetworkPolicies are allow-lists — once a Pod is selected, everything not allowed is denied.
  • Watch the namespaceSelector vs podSelector AND/OR indentation trap.
  • Always allow DNS egress when writing default-deny egress policies.

Master these patterns through hands-on repetition, and the Services & Networking domain becomes one of the fastest, most reliable sources of points on the entire CKAD.

Limited Time Offer: Get 80% off all Mock Exam Bundles | Sale ends in 7 days. Start learning today.

Claim Now