StatefulSets that name no governing service in serviceName
What does ZopNight detect here?
ZopNight flags an EKS, GKE or AKS StatefulSet whose `spec.serviceName` is empty. The governing headless Service is what gives each pod a stable DNS name of the form `web-0.nginx.default.svc.cluster.local`, so without it peers and clients cannot address individual replicas, and the field cannot be added later without recreating the StatefulSet.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-1760 · RC-1860 · RC-1960 |
| Category | reliability |
| Severity | medium |
| Metric | spec.serviceName |
| Threshold | empty or unset |
| Source | ZopNight |
| Permissions used | list statefulsets.apps · list services |
Where it applies
Stable pod identity depends on a governing service
Each StatefulSet pod gets a predictable hostname, <statefulset>-<ordinal>, such as web-0.
The DNS record that makes that name reachable comes from a headless Service. The
StatefulSets concept page
describes the domain it manages as <service>.<namespace>.svc.cluster.local, with each pod
getting <pod>.<service domain>, where the governing service is the one named in serviceName.
The same page is explicit that StatefulSets currently need a headless Service for their pods’
network identity and that you are responsible for creating it.
Clustered software leans on those names. Database replicas, consensus members and brokers often find each other by stable per-pod addresses, so a StatefulSet without a governing service can start its pods and still fail to form a cluster.
Checking your StatefulSets
kubectl get statefulsets -A -o json | jq -r ' .items[] | select((.spec.serviceName // "") == "") | "\(.metadata.namespace)/\(.metadata.name)"'For those that do set it, confirm the named Service exists and is headless:
kubectl get service <service-name> -n <namespace> -o jsonpath='{.spec.clusterIP}'A headless Service prints None.
An empty field is the only trigger
ZopNight reads the serviceName value collected for each StatefulSet and fires when it is empty
or absent. It does not look up the Service itself, so a StatefulSet naming a Service that was
later deleted, or one that is not headless, passes this check. There is no threshold or waiting
period.
Why the fix means recreating the object
The Kubernetes API server treats serviceName as immutable once a StatefulSet exists, alongside
the selector, volume claim templates and pod management policy. You cannot patch it in, which is
why the check is worth catching early. The API reference also says the service must exist before
the StatefulSet, so create it first.
A reliability gap without a price tag
The finding carries no saving. The risk is clustered applications that cannot discover their peers, and clients hard-coded to per-pod names that will not resolve.
Adding the headless service
- Create a Service with
clusterIP: Nonewhose selector matches the StatefulSet’s pod labels. - Update the StatefulSet manifest to set
serviceNameto that Service. - Delete the old object without its pods:
kubectl delete statefulset <name> -n <namespace> --cascade=orphan. - Apply the updated manifest. Existing PersistentVolumeClaims are not deleted with the StatefulSet, so the pods keep their data.
- Restart the pods one ordinal at a time, highest first, and check each resolves under the new domain before moving on.