What are Service Accounts?
A Service Account provides an identity for processes running in a Pod. While user accounts are for humans interacting with the cluster via kubectl, service accounts are for Pods and workloads that need to interact with the Kubernetes API or external services. Every namespace has a default service account, but production workloads should use dedicated service accounts with minimal permissions.
Service Account Use Cases
- API Access: Pods that need to query the Kubernetes API (operators, controllers)
- CI/CD: Automated deployment tools that manage resources
- Cloud IAM: Mapping K8s identities to cloud provider IAM roles (workload identity)
- External Services: Authenticating with external services using projected tokens
Creating Service Accounts
# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
namespace: production
labels:
app: my-application
annotations:
# AWS EKS: Map to an IAM role for S3 access
eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/my-app-role"
# GKE: Map to a Google Cloud service account
# iam.gke.io/gcp-service-account: my-app@my-project.iam.gserviceaccount.com
automountServiceAccountToken: false # Disable auto-mounting for security
# Create service account imperatively
kubectl create serviceaccount app-service-account -n production
# List service accounts
kubectl get serviceaccounts -n production
# Describe a service account
kubectl describe serviceaccount app-service-account -n production
# Create a long-lived token (for external systems)
kubectl create token app-service-account -n production --duration=8760h
Using Service Accounts in Pods
# pod-with-service-account.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-application
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: my-application
template:
metadata:
labels:
app: my-application
spec:
serviceAccountName: app-service-account
automountServiceAccountToken: true # Override the SA default for this pod
containers:
- name: app
image: myapp:1.0
ports:
- containerPort: 3000
volumeMounts:
- name: token
mountPath: /var/run/secrets/tokens
readOnly: true
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: api-token
expirationSeconds: 3600 # 1 hour
audience: "https://api.example.com"
Workload Identity (Cloud Integration)
Workload Identity allows Kubernetes service accounts to act as cloud IAM identities, eliminating the need to store and manage cloud credentials as Kubernetes Secrets. This is the recommended approach for giving Pods access to cloud resources.
AWS EKS Workload Identity (IRSA)
# Create an IAM policy
aws iam create-policy --policy-name S3ReadPolicy --policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"]
}]
}'
# Create an IAM role with trust policy for the EKS OIDC provider
eksctl create iamserviceaccount \
--name app-service-account \
--namespace production \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::123456789012:policy/S3ReadPolicy \
--approve
GKE Workload Identity
# Create a Google Cloud service account
gcloud iam service-accounts create my-app-gsa --display-name="My App SA"
# Grant permissions to the GCP service account
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:my-app-gsa@my-project.iam.gserviceaccount.com" \
--role="roles/storage.objectViewer"
# Bind the K8s SA to the GCP SA
gcloud iam service-accounts add-iam-policy-binding \
my-app-gsa@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:my-project.svc.id.goog[production/app-service-account]"
# Annotate the K8s service account
kubectl annotate serviceaccount app-service-account \
--namespace production \
iam.gke.io/gcp-service-account=my-app-gsa@my-project.iam.gserviceaccount.com
RBAC for Service Accounts
# sa-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-pod-reader
namespace: production
subjects:
- kind: ServiceAccount
name: app-service-account
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Key Takeaways
- Every Pod gets a service account — create dedicated ones instead of using the default
- Set automountServiceAccountToken to false unless the Pod needs API access
- Use Workload Identity (IRSA on EKS, Workload Identity on GKE) instead of static cloud credentials
- Projected service account tokens are time-limited and audience-bound for better security
- Combine service accounts with RBAC Roles to implement least-privilege access