How to Expose a Kubernetes Deployment with a Service
A Deployment runs pods but gives them no stable address. A ClusterIP Service is what you create to expose a Kubernetes Deployment inside the cluster - it picks up your pods through a label selector and load-balances across them.
Why you need a Service
A Kubernetes Deployment manages a set of pods, but pod IPs are ephemeral - they change
every time a pod restarts or reschedules. A Service sits in front of the pods and gives
them a stable in-cluster DNS name and IP. Without one, nothing else in the cluster can
reach your app reliably.
The three things that must line up
A ClusterIP Service connects to your pods through three pieces of wiring:
selector- must match a label on the pod template in the Deployment (e.g.app: web)port- the port the Service itself listens on (what callers dial)targetPort- the port the container is actually listening on
If the selector doesn't match any pod labels, the Service has zero endpoints and traffic goes nowhere - no error, just silence.
Writing service.yaml
Given a Deployment whose pod template has labels: {app: web} and whose container listens
on port 80:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web # must match pod template labels exactly
ports:
- port: 80
targetPort: 80 # the port your container listens on
Apply it and confirm the Service picked up endpoints:
kubectl apply -f service.yaml
kubectl get svc web
kubectl get endpoints web
A healthy output from kubectl get endpoints web shows at least one IP:PORT entry.
If the ENDPOINTS column shows <none>, the selector is wrong - compare it to the
pod labels with kubectl get pods --show-labels.
Verifying connectivity
Once endpoints are populated you can reach the Service by its DNS name from any pod in the same namespace:
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never \
-- curl -s http://web/
Or kubectl port-forward svc/web 8080:80 to hit it from your local machine.
Common mistakes
- Selector typo -
app: Webvsapp: webis a mismatch; labels are case-sensitive. - Wrong targetPort - the Service points at port
80but the container listens on8080; curl gets aConnection refusedfrom inside the pod. - Missing Service entirely -
kubectl get svcreturns nothing for the name whilekubectl get podsshows the Deployment running. Create and apply the Service manifest to wire it up.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Writing a ClusterIP Service manifest with selector and targetPort
- Verifying endpoint wiring with kubectl get endpoints
- Diagnosing a Service with zero endpoints by checking pod labels
FAQ
How do I expose a Kubernetes Deployment inside the cluster?
Create a ClusterIP Service with a selector that matches the Deployment's pod labels and a targetPort that matches the container's listening port, then apply it with kubectl apply -f service.yaml.
Why does my Kubernetes Service have no endpoints?
The selector in the Service spec does not match the labels on the pods. Run kubectl get pods --show-labels and compare them to the selector in your Service. Labels are case-sensitive.
What is the difference between port and targetPort in a Kubernetes Service?
port is what other pods dial to reach the Service. targetPort is the port the container inside the pod is actually listening on. They can differ - for example, port 80 can forward to targetPort 8080.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →