Service Accounts
Summary
- A ServiceAccount is the identity a pod presents to the Kubernetes API server, distinct from human users.
- Insight: modern clusters mount short-lived, audience-scoped projected tokens, not long-lived Secret tokens.
- Key Concepts: ServiceAccount objects, projected token volumes, audiences, and RBAC bindings that grant the account permissions.
- When to Use: any time a workload calls the API server, a cloud API, or another service that trusts cluster-issued tokens.
- Limitations: a ServiceAccount is namespaced, its default token targets only the API server, and it has no permissions until you bind a Role to it.
Recipe
Create a dedicated ServiceAccount per workload, bind the minimum Role, and reference it from the pod spec.
kubectl create serviceaccount checkout --namespace paymentsapiVersion: v1
kind: Pod
metadata:
name: checkout
namespace: payments
spec:
serviceAccountName: checkout
automountServiceAccountToken: true
containers:
- name: app
image: registry.example.com/checkout@sha256:abc123Bind a narrow Role so the account can read only what it needs.
Working Example
Here is a complete, minimal setup: a ServiceAccount, a Role, a RoleBinding, and a Deployment that uses it.
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: payments
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: payments
name: config-reader
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: payments
name: checkout-config
subjects:
- kind: ServiceAccount
name: checkout
namespace: payments
roleRef:
kind: Role
name: config-reader
apiGroup: rbac.authorization.k8s.ioSet automountServiceAccountToken back on only for the pod that needs it.
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout
namespace: payments
spec:
replicas: 2
selector:
matchLabels: { app: checkout }
template:
metadata:
labels: { app: checkout }
spec:
serviceAccountName: checkout
automountServiceAccountToken: true
containers:
- name: app
image: registry.example.com/checkout@sha256:abc123The pod now authenticates to the API server as system:serviceaccount:payments:checkout and can read configmaps in payments, nothing more.
Deep Dive
How pod identity works
Every namespace ships with a default ServiceAccount, and pods that name none inherit it.
The API server projects a signed JWT into the pod, and the kubelet keeps it fresh.
The token's sub claim resolves to system:serviceaccount:<namespace>:<name>, which is the subject RBAC evaluates.
Projected token volumes
Since the BoundServiceAccountTokenVolume feature graduated, tokens are delivered through a projected volume, not a persistent Secret.
volumes:
- name: api-token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: apiexpirationSecondsbounds the token lifetime, and the kubelet rotates it before expiry.audiencescopes the token to a specific recipient, so a token meant for one service is rejected by another.- The token is bound to the pod's lifetime and is invalidated when the pod is deleted.
Turning off automounting
Most workloads never call the API server, so they should not carry a token.
Set automountServiceAccountToken: false on the ServiceAccount or pod to drop the mount.
This shrinks the blast radius if a container is compromised.
Tokens for external systems
The TokenRequest API issues audience-scoped tokens that external systems can verify against the cluster's OIDC discovery document.
Cloud providers use this for workload identity federation, letting a pod assume a cloud role without any static cloud key.
A pod requests such a token by declaring the target audience on a projected volume, and the receiving service validates the signature and audience before trusting it.
Because these tokens are short-lived and audience-bound, a token minted for one downstream cannot be replayed against another.
Inspecting a mounted token
You can confirm what a pod actually receives without leaving the cluster.
kubectl exec -n payments deploy/checkout -- \
cat /var/run/secrets/kubernetes.io/serviceaccount/token- The path is the default projected mount location for the API-server audience token.
- Decoding the JWT payload shows the
sub,aud, andexpclaims. - The
subclaim is the exact RBAC subject the API server evaluates. - If the file is absent, automounting is off for that pod, which is the intended state for most workloads.
Gotchas
Do not bind Roles to the default ServiceAccount, since every pod without an explicit account inherits that access.
Legacy long-lived Secret tokens still exist if you manually create a kubernetes.io/service-account-token Secret, but avoid them - they do not expire and are a standing credential.
A ServiceAccount with no binding is not an error, it simply has zero API permissions, which is the correct default.
Cross-namespace binding requires the namespace field on the subject, and omitting it silently targets the binding's own namespace.
Granting create on serviceaccounts/token lets a subject mint tokens for other accounts, which is an escalation path worth auditing.
Alternatives
Use a per-workload ServiceAccount when a pod must call the API server or a token-trusting service - this is the default recommendation.
Use cloud workload identity federation, layered on projected tokens, when a pod needs cloud APIs without static keys.
Use automountServiceAccountToken: false with no bindings when a workload never talks to the API, which is the majority of application pods.
Use a human OIDC identity, not a ServiceAccount, for interactive kubectl access - ServiceAccounts are for workloads.
FAQs
What identity does a pod use if I set nothing?
The default ServiceAccount of its namespace, which should carry no meaningful permissions.
Are ServiceAccount tokens still stored as Secrets? No, by default they are projected, short-lived tokens. Long-lived Secret tokens exist only if you create them explicitly.
How do I give a ServiceAccount permissions? Bind a Role or ClusterRole to it with a RoleBinding or ClusterRoleBinding, exactly as you would for a user.
Can one ServiceAccount be used across namespaces? No, a ServiceAccount is namespaced. A binding in another namespace can reference it, but the account object lives in one namespace.
How do I stop a container from receiving a token?
Set automountServiceAccountToken: false on the pod or the ServiceAccount.
Related
- How Kubernetes Decides Who Can Do What
- RBAC Basics
- OIDC & SSO for kubectl
- RBAC Auditing
- RBAC Best Practices
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).