Search across all documentation pages
clusterIP: None), DNS A/AAAA records, StatefulSet identity, SRV records.Use a normal ClusterIP Service by default.
Add a headless Service only when a client must address specific pods.
# Standard ClusterIP: get a VIP + DNS
kubectl expose deployment web --port=80 --target-port=8080
# Headless: no VIP, pod IPs returned in DNS
kubectl apply -f headless-service.yamlThe deciding question: does the caller need one address or all addresses?
A StatefulSet with a headless Service gives each pod a stable DNS name.
apiVersion: v1
kind: Service
metadata:
name: cassandra
spec:
clusterIP: None
selector:
app: cassandra
ports:
- name: cql
port: 9042
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cassandra
spec:
serviceName: cassandra
replicas: 3
selector:
matchLabels:
app: cassandra
template:
metadata:
labels:
app: cassandra
spec:
containers:
- name: cassandra
image: cassandra:5.0
ports:
- name: cql
containerPort: 9042The serviceName: cassandra field ties the StatefulSet to the headless Service.
Each pod now resolves at cassandra-0.cassandra.default.svc.cluster.local, and so on.
kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- \
nslookup cassandra.default.svc.cluster.localThat lookup returns one A record per Ready pod, letting a client connect to the whole ring.
The control plane allocates a VIP from the Service CIDR and holds it for the life of the Service.
kube-proxy programs kernel rules so packets to the VIP are DNATed to a Ready endpoint.
DNS returns exactly one record - the VIP - so the client is unaware of individual pods.
This is the right default for stateless HTTP and gRPC backends.
Setting clusterIP: None tells the control plane to skip VIP allocation.
CoreDNS then answers the Service name with one A/AAAA record per Ready endpoint.
There are no kube-proxy DNAT rules, because there is no VIP to translate.
The client receives the full list and decides which pod to talk to.
A headless Service paired with a StatefulSet yields per-pod DNS: <pod>-<ordinal>.<service>.
That name survives reschedules even though the pod IP changes.
Systems like Kafka, ZooKeeper, and Postgres replicas rely on this to find peers.
For named ports, CoreDNS also serves SRV records, exposing port and target together.
Clients that discover peers dynamically can query SRV to learn both host and port.
This is useful for clustered software that bootstraps from DNS.
The ClusterIP comes from the Service CIDR configured on the API server.
You can request a specific IP with spec.clusterIP, but it must fall inside that range and be free.
The field is immutable after creation, which is why switching between ClusterIP and headless means recreating the Service.
Dual-stack clusters can assign both an IPv4 and IPv6 ClusterIP through ipFamilyPolicy and ipFamilies.
Reach for ClusterIP whenever the client just wants "a healthy backend" and does not care which one.
Reach for headless whenever the client's logic depends on knowing the individual members.
Databases, quorum systems, and shard-aware clients almost always fall in the second bucket.
Everything stateless and request-scoped falls in the first.
DNS caching can mask membership changes - a client that caches a headless result may keep hitting a dead pod until the TTL expires.
Keep application-side DNS TTL handling honest, or use a client that re-resolves.
A headless Service does no load balancing, so if you expected even spreading you will get whatever your client does.
Publishing not-ready pods is opt-in: set publishNotReadyAddresses: true only when peers must discover each other during startup, and understand you may route to unready pods.
An empty DNS answer usually means no pods are Ready or the selector is wrong - check EndpointSlices first.
Do not firewall a headless Service by VIP; there is none, so write policy against pod labels instead.
Normal ClusterIP: pick this for stateless services that just need a load-balanced entry point.
Headless + StatefulSet: pick this for stateful, peer-aware systems needing stable identity.
Headless + client-side load balancing: pick this for gRPC clients that want to spread requests across all backends themselves.
ExternalName: pick this to alias an out-of-cluster host behind a cluster DNS name without any proxying.
When should I set clusterIP: None?
When the caller must address individual pods - StatefulSets, databases, or client-side load balancing over gRPC.
Does a headless Service load balance? No. It only returns pod IPs in DNS. Any balancing is up to the client.
Can I convert a ClusterIP Service to headless later?
clusterIP is immutable, so you delete and recreate the Service rather than editing it in place.
Why do I see unready pods in DNS?
You likely set publishNotReadyAddresses: true, or the pods flap between Ready and not-Ready. Review readiness probes.
How do I address pod number two directly?
Use its StatefulSet DNS name, <statefulset>-1.<service>.<namespace>.svc.cluster.local.
Can a headless Service still have named ports and SRV records? Yes. Named ports produce SRV records that let clients discover both the host and port of each peer from DNS.
Does a headless Service use kube-proxy? No VIP means no kube-proxy DNAT rules; routing is entirely client-driven off the DNS answers.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026