Service Accounts
Video: Day 25/40 — Kubernetes Service Account (RBAC Continued) • https://www.youtube.com/watch?v=k2iCq7IlMKM • Duration: ~16 min
Key terms
| Term | Meaning |
|---|---|
| ServiceAccount (SA) | Identity for pods/workloads |
| default SA | Auto-assigned account per namespace |
| Token | Credential mounted into a pod |
| Projected token | Short-lived, audience-bound token |
| automountServiceAccountToken | Toggle for mounting the token |
| RBAC | Binds permissions to a ServiceAccount |
Problem & solution
Pods, controllers, and CI bots also need an identity to call the API, but human user accounts aren't Kubernetes objects and don't fit automated workloads. Service accounts give non-human workloads a managed in-cluster identity.
Solution: Give in-cluster apps a ServiceAccount identity with an auto-mounted token, then grant it permissions through RBAC.
The analogy
Most badges at the port go to people, but the automated cranes and loader robots have to pass through gates too, and you obviously cannot hand a robot a human ID. So the port issues machine badges built specifically for equipment, each carrying a chip the gates can read on the robot's behalf. Kubernetes does the same: a ServiceAccount is the machine badge for a pod or workload, the auto-mounted token is its chip, and the api-server reads that token to authenticate the workload, never treating it as a human user.
Where this fits in the cluster
The same cluster entities appear in every day's notes; the diagram below shows where this day's topic fits.
Two kinds of accounts
Kubernetes splits identities into user accounts for humans and service accounts for workloads — only the latter is a real cluster object.
USER account -> humans: admins, developers, operators
(certs/OIDC, NOT a Kubernetes object)
SERVICE account -> non-humans: pods, controllers, CI bots, apps
(a real Kubernetes object you can create)
When code inside a pod needs to call the Kubernetes API, it uses a ServiceAccount, not a human user cert.
Default service account
Every namespace has a default SA, and every pod that doesn't name one gets it
mounted automatically.
kubectl get sa # SAs in current namespace
kubectl get sa -A # every namespace has its own 'default'
Create a service account
A ServiceAccount is a namespaced object you create like any other resource. Here is the real Prometheus SA: the monitoring server runs as this identity to call the Kubernetes API for service discovery.
kubectl create sa prometheus -n monitoring
kubectl get sa prometheus -n monitoring -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitoring
Give it permissions (RBAC, same as users)
A SA is just another subject in a RoleBinding/ClusterRoleBinding.
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prometheus-read
namespace: monitoring
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitoring
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Verify with impersonation:
kubectl auth can-i list pods -n monitoring \
--as=system:serviceaccount:monitoring:prometheus # -> yes
Use it from a pod
Set serviceAccountName on the pod spec so the container runs as that SA and
gets its token mounted.
apiVersion: v1
kind: Pod
metadata:
name: prometheus
namespace: monitoring
spec:
serviceAccountName: prometheus # <- pod runs as this SA
containers:
- name: prometheus
image: prom/prometheus:v2.53.0
The token is mounted at:
/var/run/secrets/kubernetes.io/serviceaccount/
token -> JWT bearer token (auto-rotated, projected)
ca.crt -> cluster CA to trust the API server
namespace -> the pod's namespace
Tokens
You can mint a bearer token for a SA on demand, useful for testing API calls or external clients.
kubectl create token prometheus -n monitoring # short-lived token (on demand)
kubectl create token prometheus -n monitoring --duration=1h
Modern Kubernetes uses short-lived, auto-rotated projected tokens instead of the old permanent Secret-based tokens.
End-to-end example: Prometheus calls the API as its ServiceAccount
The Prometheus pod authenticates with its auto-mounted SA token to discover and read pods.
Graph legend — each step maps to a concrete object or mount:
| Graph step | Maps to | What it does |
|---|---|---|
| pod runs as ServiceAccount prometheus | spec.serviceAccountName: prometheus | Binds the pod to the SA identity |
| token auto-mounted | /var/run/secrets/kubernetes.io/serviceaccount/token | Projected JWT the app sends as a Bearer token |
| AuthN token to system:serviceaccount:monitoring:prometheus | the SA token authn | Resolves the token to the SA identity |
| AuthZ RoleBinding to Role pod-reader | RoleBinding prometheus-read | Grants get/list on pods in the namespace |
| 200 OK, pod list | the allow path | Returns the pods Prometheus will scrape |
End-to-end flow
An in-cluster app authenticates to the API using its mounted ServiceAccount token.
Graph legend — each node maps to a concrete object or mount:
| Graph node | Maps to | What it does |
|---|---|---|
| Pod runs as ServiceAccount prometheus | spec.serviceAccountName: prometheus | Runs the container under the SA identity |
| Projected token auto-mounted | the projected SA token volume | Supplies a short-lived, audience-bound JWT |
| Prometheus: GET /pods with Bearer token | the app's API call | Sends the token in the Authorization header |
| api-server AuthN | the SA token authn | Resolves to system:serviceaccount:monitoring:prometheus |
| AuthZ: RoleBinding -> Role pod-reader | RoleBinding prometheus-read | Permits get/list on pods |
| 200 OK: pod list returned | the allow path | Prometheus receives the pods to scrape |
Key takeaways
- User accounts = humans, ServiceAccounts = workloads/bots.
- SAs are real namespaced objects; each namespace has a
defaultSA. - Grant a SA access with the same RBAC (Role/ClusterRole + binding).
- Subject form:
system:serviceaccount:<namespace>:<name>. - Pods authenticate using the projected token mounted into the container.
Checklist
- [ ] Listed default SAs across namespaces
- [ ] Created a
prometheusSA and bound it to a Role - [ ] Verified with
--as=system:serviceaccount:monitoring:prometheus - [ ] Ran a pod with
serviceAccountNameand found the mounted token