Services cheatsheet.
Service types
- ClusterIP (default): internal IP. Reachable from within cluster.
- NodePort: ClusterIP + node port (30000-32767).
- LoadBalancer: NodePort + cloud LB (AWS ELB, GCP LB).
- ExternalName: DNS CNAME to external service.
- Headless (
clusterIP: None): no LB, DNS returns pod IPs directly.
ClusterIP
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80 # service port
targetPort: 8000 # container port
protocol: TCP
# Inside cluster:
curl http://web/
curl http://web.default.svc.cluster.local/
NodePort
spec:
type: NodePort
ports:
- port: 80
targetPort: 8000
nodePort: 30080 # optional; auto-assigned otherwise
Reachable at <any-node-ip>:30080.
LoadBalancer
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8000
Cloud provider provisions external LB:
kubectl get svc web
# NAME TYPE EXTERNAL-IP PORTS
# web LoadBalancer 1.2.3.4 443:30543/TCP
ExternalName
spec:
type: ExternalName
externalName: api.external.com
http://my-service resolves via DNS to api.external.com. Useful for aliasing.
Headless service
spec:
clusterIP: None
selector: { app: db }
ports: [{ port: 5432 }]
DNS returns ALL pod IPs (one A record per pod). Used by StatefulSets.
Multi-port service
spec:
ports:
- name: http
port: 80
targetPort: 8000
- name: metrics
port: 9090
targetPort: 9090
sessionAffinity
spec:
sessionAffinity: ClientIP # sticky by client IP
Default None (random pod each request).
Endpoints
kubectl get endpoints web
# web 10.0.1.5:8000,10.0.2.7:8000,10.0.3.4:8000
Service maps to these pod IPs.
Inspecting service
kubectl get svc
kubectl get svc -o wide
kubectl describe svc web
kubectl get svc web -o yaml
DNS
<service>.<namespace>.svc.cluster.local
Within same namespace, just <service> works.
Cross-namespace:
web.default
web.default.svc
web.default.svc.cluster.local
Service without selector
apiVersion: v1
kind: Service
metadata: { name: external-db }
spec:
ports: [{ port: 5432 }]
---
apiVersion: v1
kind: Endpoints
metadata: { name: external-db }
subsets:
- addresses: [{ ip: 192.168.1.50 }]
ports: [{ port: 5432 }]
Service points to external endpoint, exposed within cluster.
EndpointSlices (modern)
kubectl get endpointslices
Newer API, more scalable than Endpoints. Same idea.
Network policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: web-policy }
spec:
podSelector:
matchLabels: { app: web }
policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector: { matchLabels: { app: api } }
ports: [{ port: 8000 }]
egress:
- to:
- podSelector: { matchLabels: { app: db } }
ports: [{ port: 5432 }]
- ports: [{ port: 53, protocol: UDP }] # DNS
By default, all traffic allowed. Network policy restricts (requires CNI that supports it: Calico, Cilium).
externalTrafficPolicy
spec:
type: LoadBalancer
externalTrafficPolicy: Local # preserve client source IP
Cluster (default): rebalances, masks source IP. Local: routes only to local pods.
allocateLoadBalancerNodePorts
spec:
type: LoadBalancer
allocateLoadBalancerNodePorts: false
Some cloud LBs route directly to pods (no NodePort needed).
ipFamilyPolicy (dual-stack)
spec:
ipFamilyPolicy: PreferDualStack
ipFamilies: [IPv4, IPv6]
Inspecting from inside
kubectl run debug --image=nicolaka/netshoot -it --rm -- bash
# Inside:
nslookup web
nslookup web.default.svc.cluster.local
curl http://web/
Common patterns
Internal API:
type: ClusterIP # only reachable within cluster
Public API:
type: LoadBalancer
Or use Ingress (next cheatsheet).
Database:
type: ClusterIP # never expose DB publicly
Common mistakes
- Forgetting
selector→ no endpoints. - Service port and pod port mixed up (
portvstargetPort). - LoadBalancer in dev cluster → never provisions.
- Exposing DB as NodePort/LoadBalancer.
- Selectors not matching pod labels.
Read this next
If you want my service + network policy templates, they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .