ServiceAccounts
ServiceAccounts provide an identity for pods and applications running inside your Kubernetes cluster. Think of a ServiceAccount as a work badge that identifies which application is making requests to the Kubernetes API.
What Are ServiceAccounts?
ServiceAccounts are Kubernetes resources that represent an identity for workloads. Unlike user accounts (which are managed outside Kubernetes), ServiceAccounts are namespace-scoped resources managed by Kubernetes itself.
Every pod runs with a ServiceAccount. If you don’t specify one, Kubernetes automatically assigns the default ServiceAccount in the pod’s namespace.
How ServiceAccounts Work
When a pod is created with a ServiceAccount:
- Kubernetes creates a token (stored as a Secret)
- The token is automatically mounted into the pod at
/var/run/secrets/kubernetes.io/serviceaccount/ - The pod uses this token to authenticate to the Kubernetes API
- RBAC rules determine what the ServiceAccount can do
Creating ServiceAccounts
Basic ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: production
This creates a ServiceAccount with no special permissions. It can authenticate but needs RBAC permissions to perform actions.
ServiceAccount with Image Pull Secrets
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: production
imagePullSecrets:
- name: registry-credentials
This ServiceAccount can pull images from private registries using the specified image pull secret.
Using ServiceAccounts in Pods
Explicit ServiceAccount Assignment
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: production
spec:
serviceAccountName: my-app # Use this ServiceAccount
containers:
- name: app
image: nginx:latest
Automatic Token Mounting
Kubernetes automatically mounts three files into each pod:
- token - The authentication token at
/var/run/secrets/kubernetes.io/serviceaccount/token - ca.crt - The cluster’s CA certificate at
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - namespace - The pod’s namespace at
/var/run/secrets/kubernetes.io/serviceaccount/namespace
You can disable automatic mounting:
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app
automountServiceAccountToken: false # Disable automatic mounting
containers:
- name: app
image: nginx:latest
Token Management
Token Types
Kubernetes supports two types of ServiceAccount tokens:
- Legacy tokens - Long-lived tokens stored as Secrets (deprecated in Kubernetes 1.24+)
- TokenRequest API - Short-lived tokens obtained via the TokenRequest API (recommended)
Creating Tokens (Kubernetes 1.24+)
apiVersion: v1
kind: Secret
metadata:
name: my-app-token
namespace: production
annotations:
kubernetes.io/service-account.name: my-app
type: kubernetes.io/service-account-token
However, the recommended approach is to use the TokenRequest API programmatically or let pods automatically mount tokens.
Using Tokens in Applications
Applications can read the token from the mounted file:
# Inside a pod
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
# Make API request
curl -X GET \
--cacert $CA_CERT \
-H "Authorization: Bearer $TOKEN" \
https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods
RBAC Integration
ServiceAccounts need RBAC permissions to perform actions. Here’s a complete example:
# 1. Create ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: config-reader
namespace: production
---
# 2. Create Role with permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: config-reader-role
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
---
# 3. Bind Role to ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: config-reader-binding
namespace: production
subjects:
- kind: ServiceAccount
name: config-reader
namespace: production
roleRef:
kind: Role
name: config-reader-role
apiGroup: rbac.authorization.k8s.io
---
# 4. Use ServiceAccount in Pod
apiVersion: v1
kind: Pod
metadata:
name: my-app
namespace: production
spec:
serviceAccountName: config-reader
containers:
- name: app
image: nginx:latest
Best Practices
- One ServiceAccount per application - Don’t reuse ServiceAccounts across different applications
- Namespace isolation - Create ServiceAccounts in the same namespace as your workloads
- Minimal permissions - Grant only the permissions each application needs
- Naming convention - Use descriptive names like
app-nameorcomponent-name - Disable automount when not needed - Set
automountServiceAccountToken: falseif the pod doesn’t need API access - Rotate tokens - Use short-lived tokens when possible (TokenRequest API)
Common Patterns
Read-Only ServiceAccount
For applications that only need to read resources:
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitor
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: monitor-role
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: monitor-binding
namespace: production
subjects:
- kind: ServiceAccount
name: monitor
namespace: production
roleRef:
kind: Role
name: monitor-role
apiGroup: rbac.authorization.k8s.io
CI/CD ServiceAccount
For CI/CD systems that deploy applications:
apiVersion: v1
kind: ServiceAccount
metadata:
name: cicd-deployer
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: deployer-role
rules:
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: deployer-binding
namespace: production
subjects:
- kind: ServiceAccount
name: cicd-deployer
namespace: production
roleRef:
kind: Role
name: deployer-role
apiGroup: rbac.authorization.k8s.io
Troubleshooting
Check ServiceAccount exists:
kubectl get serviceaccount my-app -n production
View ServiceAccount details:
kubectl describe serviceaccount my-app -n production
Check what permissions a ServiceAccount has:
kubectl auth can-i --list \
--as=system:serviceaccount:production:my-app \
--namespace=production
Verify token is mounted in pod:
kubectl exec -it <pod-name> -n production -- \
ls -la /var/run/secrets/kubernetes.io/serviceaccount/
Test API access from pod:
kubectl exec -it <pod-name> -n production -- \
curl -k https://kubernetes.default.svc/api/v1/namespaces/production/pods
Security Considerations
- Token exposure - Tokens in pods can be read by anyone with pod exec access
- Token lifetime - Legacy tokens don’t expire; use TokenRequest API for short-lived tokens
- Permission scope - ServiceAccounts should have minimal permissions
- Namespace boundaries - ServiceAccounts are namespace-scoped, but ClusterRoles can grant cluster-wide access
See Also
- Authentication & Authorization - Overview of AuthN and AuthZ
- RBAC - Role-Based Access Control
- Least Privilege - Implementing minimal permissions
- Secrets Management - Managing ServiceAccount tokens