✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Headless Service Behavior

Kubernetes Headless Services enable direct pod communication without DNS resolution, operating within the Kubernetes cluster's internal network.

Kubernetes Headless Service Behavior is the set of DNS and endpoint-resolution semantics that apply to a Service resource created with clusterIP: None. Instead of allocating a single virtual IP and load-balancing traffic through kube-proxy, a headless Service exposes the individual network identities of the Pods it selects, allowing clients to discover and connect to specific Pods directly rather than through a proxied, load-balanced front end.


Core Mechanics

Absence of a Cluster IP

A headless Service is declared by explicitly setting clusterIP: None in the Service spec. This instructs the API server not to allocate a virtual IP from the Service CIDR range. Because there is no cluster IP, kube-proxy does not program any iptables or IPVS rules for that Service, and no load-balancing layer sits between the client and the backend Pods.

apiVersion: v1
kind: Service
metadata:
  name: headless-db
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432

DNS Resolution Model

When a headless Service has a selector, the cluster DNS (CoreDNS) returns multiple A/AAAA records for the Service name — one for each Ready Pod backing it — instead of a single record pointing at a virtual IP. A client performing a DNS lookup against headless-db.default.svc.cluster.local receives the full set of Pod IPs currently matching the selector, and the client (or its resolver library) is responsible for choosing which one to connect to.

Endpoints and EndpointSlice Objects

Kubernetes still creates Endpoints and EndpointSlice objects for a headless Service with a selector. These objects track the IP addresses of matching Ready Pods and are kept in sync by the endpoints controller. CoreDNS reads from EndpointSlice objects to build its DNS answers, so any change in Pod readiness is reflected in subsequent DNS queries within the TTL window.


Headless Services With and Without Selectors

Selector-Based Headless Services

When a selector is present, Kubernetes automatically manages EndpointSlice membership based on Pod labels and readiness, and DNS returns one record per backing Pod. This is the typical configuration for stateful workloads that need to address individual replicas.

Selectorless Headless Services

Omitting the selector field entirely produces a different behavior: Kubernetes does not manage any Endpoints automatically. Instead, the cluster administrator (or an external controller) must create the Endpoints/EndpointSlice objects manually, or rely on ExternalName semantics. This pattern is commonly used to represent external systems (databases, legacy services) inside cluster DNS without running Pods for them.

apiVersion: v1
kind: Service
metadata:
  name: external-legacy-db
spec:
  clusterIP: None
  ports:
    - port: 5432
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-legacy-db
subsets:
  - addresses:
      - ip: 10.20.30.40
    ports:
      - port: 5432

ExternalName Services Compared

A Service of type ExternalName is conceptually related but distinct: it returns a CNAME record pointing at an external DNS name and never allocates a cluster IP either, but it does not manage Pod endpoints at all. Headless Services with selectors, by contrast, are firmly tied to in-cluster Pod IPs.


Integration With StatefulSets

Stable Network Identity for Replicas

StatefulSets rely on a headless "governing Service" to give each Pod a predictable, stable DNS name in the form <pod-name>.<service-name>.<namespace>.svc.cluster.local. Because the Service has no cluster IP, DNS lookups against the per-Pod hostname resolve directly to that Pod's IP address, which is essential for peer-discovery protocols used by distributed databases and coordination services (etcd, Cassandra, ZooKeeper, Kafka).

apiVersion: v1
kind: Service
metadata:
  name: cassandra
spec:
  clusterIP: None
  selector:
    app: cassandra
  ports:
    - 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:4.1
          ports:
            - containerPort: 9042

Pod Hostname and Subdomain Fields

For per-Pod DNS records to be created, the Pod template must set spec.hostname and spec.subdomain (StatefulSets configure this automatically based on serviceName and the Pod ordinal), and the subdomain must match the name of the governing headless Service.

Peer Discovery Patterns

Applications running inside such Pods typically perform a DNS query for the bare Service name (returning all Pod IPs) at startup to discover peers, then use the stable per-Pod hostnames for ongoing membership and gossip protocols, tolerating individual Pod restarts without losing their network identity.


Client-Side Load Balancing Implications

Why Kube-Proxy Is Bypassed

Because no virtual IP exists, none of kube-proxy's iptables, IPVS, or userspace proxy modes apply to traffic destined for a headless Service. This removes the connection-tracking and NAT overhead of a normal Service but shifts responsibility for load distribution to the client.

DNS Round-Robin and Its Limits

Many client libraries resolve a hostname once and cache the result, meaning DNS round-robin across multiple A records provides only a coarse approximation of load balancing and does not react quickly to topology changes. Applications that require accurate, low-latency load balancing across headless-Service backends often re-resolve DNS periodically or use client-side libraries (gRPC's dns resolver, for example) that natively support multiple addresses per name and pick a new one per connection or per RPC.

Readiness Gating of DNS Records

Only Pods considered Ready by their readiness probes are included in the EndpointSlice, and therefore in DNS answers, for a headless Service. A Pod that fails its readiness probe is removed from the address set until it becomes Ready again, giving clients an automatic mechanism to avoid unhealthy backends even without a proxying layer.


Common Use Cases

Peer-to-Peer Clustered Databases

Distributed data stores that implement their own gossip or consensus protocol (Cassandra, MongoDB replica sets, CockroachDB) use headless Services so each node can be addressed individually for cluster formation, rebalancing, and leader election.

Custom Client-Side Load Balancing

Systems built around gRPC or other protocols with native multi-address awareness prefer headless Services to obtain the full set of backend addresses and implement their own connection-level load balancing, avoiding an extra network hop through a proxy.

Service Discovery for Sidecars and Meshes

Some service mesh and sidecar-proxy architectures use headless Services as the raw address source, layering their own traffic management (mTLS, retries, circuit breaking) on top of the direct Pod IPs rather than relying on kube-proxy's Layer 4 load balancing.


Operational Considerations

DNS Caching and TTLs

CoreDNS returns short TTLs for Service records, but resolver libraries and operating-system stub resolvers may still cache more aggressively. Applications sensitive to membership changes should be tested against their actual resolution and caching behavior, not assumed defaults.

Observability

Because headless Services do not appear as a single virtual IP in cluster networking, tools that expect Layer 4 load-balancer metrics (such as service-level connection counts from kube-proxy) provide little visibility for headless traffic; application-level or per-Pod metrics become the primary signal.

Interaction With Network Policies

NetworkPolicy resources still apply based on Pod selectors and IP addresses, independent of whether the Service used to discover those Pods is headless or not, so headless Service usage does not change how traffic is authorized at the network layer.