All Articles

What is a Kubernetes Service Account? RBAC Explained Simply (2026)

Service Accounts and RBAC confuse most beginners. Here's what they are, why they exist, and how to set them up correctly.

DevOpsBoysApr 5, 20263 min read
Share:Tweet

Every Kubernetes beginner eventually runs into this error:

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" 
cannot list resource "pods" in API group "" in the namespace "default"

This is a Service Account + RBAC issue. Here's what's happening and how to fix it.


What is a Service Account?

Kubernetes has two types of accounts:

  • User accounts — for humans (you using kubectl)
  • Service accounts — for applications running inside the cluster

When a pod needs to talk to the Kubernetes API (to list pods, create deployments, read secrets), it needs an identity. That identity is a Service Account.

Every pod gets a Service Account automatically — if you don't specify one, it uses the default service account in its namespace.


Why Do Pods Need to Talk to the API?

Common cases:

  • ArgoCD — needs to create/update/delete any resource in the cluster
  • Prometheus — needs to list pods and services to discover scrape targets
  • CI/CD runners — need to deploy applications
  • Custom operators — manage custom resources
  • Your app — might need to read ConfigMaps or list other pods

The RBAC Model

RBAC (Role-Based Access Control) controls who can do what.

Four key resources:

ServiceAccount → (bound by) → RoleBinding → Role
                                               ↓
                                         (permissions to do)
                                         list pods, create deployments...
  • Role — defines permissions within a namespace
  • ClusterRole — defines permissions cluster-wide
  • RoleBinding — binds a Role to a ServiceAccount in a namespace
  • ClusterRoleBinding — binds a ClusterRole cluster-wide

Step-by-Step Example

Let's give an application permission to list pods in its namespace.

Step 1: Create a Service Account

yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: myapp-sa
  namespace: production

Step 2: Create a Role with the permissions needed

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]

Step 3: Bind the Role to the Service Account

yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: myapp-pod-reader
  namespace: production
subjects:
  - kind: ServiceAccount
    name: myapp-sa
    namespace: production
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Step 4: Use the Service Account in your Pod/Deployment

yaml
spec:
  serviceAccountName: myapp-sa
  containers:
    - name: myapp
      image: myapp:v1.0

Now your app can list pods in the production namespace — and nothing else.


Common Verbs

yaml
verbs: ["get", "list", "watch"]           # read only
verbs: ["create", "update", "patch"]      # write
verbs: ["delete"]                         # delete
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]  # full CRUD

ClusterRole vs Role: When to Use Which

Use Role (namespaced) when:

  • Your app only needs access to resources in its own namespace
  • Following least privilege (always prefer this)

Use ClusterRole when:

  • You need to access cluster-wide resources (nodes, namespaces, persistent volumes)
  • You need to read resources across all namespaces (Prometheus scraping all pods)
yaml
# ClusterRole for Prometheus to discover targets cluster-wide
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
rules:
  - apiGroups: [""]
    resources: ["nodes", "pods", "services", "endpoints"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["extensions", "networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "list", "watch"]

Debugging RBAC Issues

bash
# Check if a service account can do something
kubectl auth can-i list pods \
  --as=system:serviceaccount:production:myapp-sa \
  -n production
 
# Check what roles are bound to a service account
kubectl get rolebindings -n production -o yaml | grep -A 5 myapp-sa
 
# Describe the service account
kubectl describe serviceaccount myapp-sa -n production

The Default Service Account Problem

By default, every pod uses the default service account, which has no permissions. This is good for security — pods can't talk to the API without explicit grants.

Disable auto-mounting if your app doesn't need API access:

yaml
spec:
  automountServiceAccountToken: false

This improves security — if the pod is compromised, the attacker can't use the service account token to access the cluster API.


Resources

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments