Why Do StatefulSets Need a Headless Service?
A Headless Service (clusterIP: None) gives each StatefulSet Pod a stable DNS record in the form <pod-name>.<service-name>.<namespace>.svc.cluster.local. This is required because StatefulSet Pods need individually addressable, stable network identities.
Detailed Answer
A Headless Service is a Service with clusterIP: None. Instead of providing a single virtual IP that load-balances across Pods, it creates individual DNS records for each Pod. StatefulSets require a Headless Service because each Pod needs a stable, unique network identity.
Why Regular Services Are Not Enough
A regular ClusterIP Service gives you a single DNS entry (e.g., mysql.default.svc.cluster.local) that round-robins across all backend Pods. This is perfect for stateless apps but breaks stateful ones:
- A Kafka consumer needs to connect to a specific broker
- A MySQL replica needs to find the primary by hostname
- An etcd member must announce its own address to the cluster
Creating a Headless Service
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
labels:
app: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
The key is clusterIP: None. This tells Kubernetes not to allocate a virtual IP and instead return the Pod IPs directly in DNS responses.
How DNS Records Work
With a StatefulSet named mysql and a Headless Service named mysql-headless in the default namespace, Kubernetes creates:
Per-Pod A records:
mysql-0.mysql-headless.default.svc.cluster.local → 10.244.1.5
mysql-1.mysql-headless.default.svc.cluster.local → 10.244.2.8
mysql-2.mysql-headless.default.svc.cluster.local → 10.244.3.3
Service-level A record (returns all Pod IPs):
mysql-headless.default.svc.cluster.local → 10.244.1.5, 10.244.2.8, 10.244.3.3
SRV records:
_tcp.mysql-headless.default.svc.cluster.local → mysql-0, mysql-1, mysql-2
Linking the StatefulSet to the Service
The StatefulSet references the Headless Service via the serviceName field:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql-headless" # Must match the Headless Service name
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
Common Pattern: Two Services
In production, you often create two Services for a StatefulSet:
- Headless Service — for peer discovery and the
serviceNamefield - Regular ClusterIP Service — for client connections that should be load-balanced
# Headless Service for peer discovery
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
# Regular Service for client access
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
Clients connect to mysql.default.svc.cluster.local for load-balanced access, while replicas use mysql-0.mysql-headless.default.svc.cluster.local for direct peer communication.
DNS Stability Across Rescheduling
When a StatefulSet Pod is rescheduled to a different node, it gets a new IP address. However, the DNS record is updated to point to the new IP, so the hostname mysql-0.mysql-headless remains valid. This is the key benefit — applications reference Pods by DNS name, not IP, and the name never changes.
Why Interviewers Ask This
Interviewers ask this to test whether you understand the networking model behind StatefulSets and how individual Pods are discovered by peer applications in a cluster.
Common Follow-Up Questions
Key Takeaways
- The Headless Service provides DNS-based identity, not load balancing — each Pod gets its own DNS record.
- The StatefulSet's serviceName field must reference a Headless Service for Pod DNS to work.
- You can create an additional regular Service for load-balanced client access alongside the Headless Service.