Services and Ingress: exposing an app in Kubernetes
A Service gives a changing set of Pods one stable address. ClusterIP is internal, NodePort and LoadBalancer reach outside, and an Ingress routes HTTP traffic by host and path.
Not checked by a trusted editor yet Last edited Outdated?
Pods come and go and their IP addresses change. A Service solves this: it selects Pods by label and gives them one stable virtual IP and DNS name. Traffic sent to the Service is spread across the Pods that are currently ready.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
port is what clients connect to; targetPort is the port the container listens on. If you leave out targetPort, it defaults to the same value as port.
Service types
- ClusterIP (default): reachable only inside the cluster. Right for databases, internal APIs and anything behind an Ingress.
- NodePort: also opens the same port on every node’s IP. The port is taken from a range that defaults to 30000-32767. Useful for labs and for putting your own external load balancer in front.
- LoadBalancer: asks the cloud provider (or a bare-metal implementation such as MetalLB) for an external load balancer that forwards to the Service.
- ExternalName: no proxying at all; cluster DNS returns a CNAME to an outside hostname.
kubectl expose deployment web --port=80 --target-port=8080 --type=NodePort
kubectl get service web
kubectl get endpointslices -l kubernetes.io/service-name=web
If the endpoint list is empty, the selector does not match any ready Pod. That is the most common reason a Service “does not work”.
DNS names
With cluster DNS, every Service gets a name of the form <service>.<namespace>.svc.cluster.local. Pods in the same namespace can simply use web; Pods elsewhere use web.shop or the full name.
Ingress: HTTP routing
An Ingress routes HTTP and HTTPS traffic from outside to Services, by hostname and URL path, and can terminate TLS. One external entry point can then serve many applications.
An Ingress object does nothing on its own. You must run an Ingress controller in the cluster, and the ingressClassName field picks which one handles the object. Controllers differ in their annotations and behavior, so check your controller’s documentation.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
ingressClassName: example-class
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
pathType is Prefix, Exact or ImplementationSpecific. The TLS certificate lives in a Secret of type kubernetes.io/tls in the same namespace.
Ingress or Gateway API?
The Kubernetes documentation states that the Ingress API is frozen: it remains generally available and is not planned for removal, but it receives no new features. For new designs the project recommends the Gateway API, which splits the job into Gateway and HTTPRoute objects and supports more protocols. Existing Ingress setups keep working.
Quick troubleshooting
kubectl get endpointslices -l kubernetes.io/service-name=web: are there ready endpoints?kubectl port-forward service/web 8080:80: does the Service answer from your machine?kubectl describe ingress web: does it show an address, and are there events from the controller?
Official documentation
See also
- 0135 Deploy your first app on Kubernetes A step-by-step tutorial: write a Deployment and a Service in YAML, apply them, reach the app, add health probes, roll out a… Beginner · How-to · Linux Article Ingress & networking · Workloads & services 15 Oct 2026
- 0153 Expose apps with OpenShift Routes: edge, passthrough and re-encrypt TLS A Route publishes a service on a host name through the OpenShift router. Learn when to use plain HTTP, edge, passthrough or… Intermediate · How-to · Any system Article Deploying applications · Ingress & networking 15 Oct 2026
History
Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.
- Commitpedia +3,320 chars