What is a ServiceAccount, and how do pods use them to authenticate to the API server?
Quick Answer
A ServiceAccount is an identity for processes running inside Pods to use when talking to the Kubernetes API server — distinct from a human User account, which Kubernetes doesn't manage as an object at all (it's handled by an external authentication mechanism). Every Pod runs with a ServiceAccount (defaulting to a namespace's `default` ServiceAccount if none is specified), and Kubernetes automatically mounts that ServiceAccount's credentials (a token) into the Pod, letting any code inside the Pod authenticate to the API server as that identity, subject to whatever RBAC permissions are bound to it.
Detailed Answer
Why Pods need their own identity
Some applications running inside a Pod need to talk to the Kubernetes API server themselves — a custom controller watching for changes to a Custom Resource, a CI/CD tool creating new Deployments, or simply an application that needs to look up its own Pod's metadata. This requires an identity to authenticate as, distinct from any human user's own credentials — that's exactly what a ServiceAccount provides.
Creating and using a ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-manager
namespace: production
apiVersion: v1
kind: Pod
metadata:
name: my-controller
spec:
serviceAccountName: pod-manager # explicitly assign this ServiceAccount
containers:
- name: controller
image: my-controller:1.0
If serviceAccountName isn't specified, the Pod automatically uses that namespace's default ServiceAccount — a detail worth knowing, since it means every Pod always authenticates as some identity, even if you never explicitly thought about which one.
How the credential actually gets into the Pod
Kubernetes automatically mounts a projected volume into every Pod at /var/run/secrets/kubernetes.io/serviceaccount/, containing a short-lived, auto-rotating bound service account token (a JWT), along with the cluster's CA certificate and the current namespace — any code inside the container can read this token and present it as a bearer token when calling the API server directly.
# Inside a Pod, this is how application code (or a Kubernetes client library)
# authenticates to the API server as the Pod's ServiceAccount:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -H "Authorization: Bearer $TOKEN" \
--cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
https://kubernetes.default.svc/api/v1/namespaces/production/pods
Most Kubernetes client libraries (used when building custom controllers/Operators — see the extensibility topic) handle this automatically via "in-cluster config" detection, so application code rarely constructs these requests by hand.
Binding permissions to a ServiceAccount
A bare ServiceAccount has no permissions by default — it must be granted permissions the same way any other RBAC subject is, via a RoleBinding or ClusterRoleBinding:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-manager-binding
namespace: production
subjects:
- kind: ServiceAccount
name: pod-manager
namespace: production
roleRef:
kind: Role
name: pod-editor
apiGroup: rbac.authorization.k8s.io
Why the default ServiceAccount should almost never be granted broad permissions
Because every Pod that doesn't explicitly specify a ServiceAccount silently uses default, granting broad permissions to a namespace's default ServiceAccount effectively grants those permissions to every Pod in that namespace, including ones that never intended to need API access at all — a common, easy-to-introduce security misconfiguration. Best practice is to leave default unprivileged, and create dedicated, narrowly-scoped ServiceAccounts (with correspondingly narrow RoleBindings) for the specific Pods that genuinely need API access.
disabling auto-mounting when not needed
spec:
automountServiceAccountToken: false
For Pods that don't need to talk to the API server at all (the majority of ordinary application workloads), explicitly disabling the automatic token mount removes an unnecessary credential from the Pod's filesystem entirely — a small but meaningful hardening step, reducing what an attacker could exfiltrate from a compromised container that had no legitimate need for API access in the first place.