Fix Kubernetes Forbidden Error: User cannot create resource (RBAC)

intermediate☸️ Kubernetes2026-03-21| Kubernetes 1.20+, kubectl CLI, any cloud provider (EKS, GKE, AKS) or self-hosted cluster

Error Message

Error from server (Forbidden): pods "my-pod" is forbidden: User "system:serviceaccount:default:default" cannot create resource "pods" in API group "" in the namespace "default"
#kubernetes#rbac#permission#forbidden#serviceaccount

The Error

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

This hits whenever a pod, CI/CD pipeline, or kubectl command tries something the current identity isn't allowed to do. Focus on User "system:serviceaccount:default:default" β€” that's a ServiceAccount named default in the default namespace, not a human user. The fix: create a Role (or ClusterRole) with the right permissions, then bind it to that identity.

What's Actually Happening

Kubernetes RBAC denies everything by default. No Role binding = no access, full stop. Any pod or pipeline running under an unbound ServiceAccount will hit this wall the moment it touches the API.

The error message is self-documenting β€” it tells you exactly what to grant:

  • Who: system:serviceaccount:default:default β€” ServiceAccount default in namespace default
  • What: create on resource pods
  • Where: API group "" (core API group), namespace default

Step-by-Step Fix

Step 1 β€” Check what permissions already exist

Worth checking first β€” the binding might already be there but scoped wrong. Save yourself the duplicate config:

# Check RoleBindings in the namespace
kubectl get rolebindings -n default -o wide

# Check ClusterRoleBindings
kubectl get clusterrolebindings -o wide | grep default

# Test the exact permission from the error
kubectl auth can-i create pods --as=system:serviceaccount:default:default -n default

If that last command returns no, keep going.

Step 2 β€” Create a Role with the required permissions

Namespace-scoped access? Use a Role. This one maps directly to what the error reported β€” create on pods in default:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-manager
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "create", "delete", "watch"]
kubectl apply -f role-pod-manager.yaml

Step 3 β€” Bind the Role to the ServiceAccount

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-manager-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: Role
  name: pod-manager
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f rolebinding-pod-manager.yaml

Step 4 β€” For cluster-wide access, use ClusterRole + ClusterRoleBinding

Controllers, operators, and monitoring agents typically need access across multiple namespaces. For those, swap a Role for a ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-manager-global
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "pods/status"]
  verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: ["apps"]
  resources: ["deployments", "replicasets"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: pod-manager-global-binding
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: pod-manager-global
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f clusterrole-binding.yaml

Step 5 β€” If you're running kubectl locally (not a ServiceAccount)

When the error shows a regular user like User "john@example.com" instead of a ServiceAccount, bind the role directly to that user:

kubectl create clusterrolebinding john-admin \
  --clusterrole=cluster-admin \
  --user=john@example.com

Don't use cluster-admin in production. It grants full read/write access to every resource in the cluster β€” one misconfigured pod becomes a serious blast radius. Create a scoped ClusterRole with only what's needed.

Verify the Fix

# Confirm the permission is now granted
kubectl auth can-i create pods --as=system:serviceaccount:default:default -n default
# Expected: yes

# See all verbs the ServiceAccount has on pods
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default | grep pods

# Re-run the failed command
kubectl run my-pod --image=nginx -n default

Tips

Use a dedicated ServiceAccount, not the default one

Every pod that doesn't specify a serviceAccountName runs as default. Grant that SA permissions and you've given those permissions to every pod in the namespace β€” including ones deployed later by other teams. Create a dedicated SA per workload instead:

kubectl create serviceaccount my-app -n default

# Then reference it in your Pod spec:
# spec:
#   serviceAccountName: my-app

Read the error message carefully β€” it tells you exactly what to fix

The format is always: User "X" cannot VERB resource "Y" in API group "Z" in namespace "N". Translate that directly into rules in your Role:

  • VERB β†’ goes into verbs
  • resource "Y" β†’ goes into resources
  • API group "Z" β†’ goes into apiGroups (empty string "" = core group)

Common API groups reference

  • "" β€” core: pods, services, configmaps, secrets, persistentvolumeclaims
  • apps β€” deployments, replicasets, statefulsets, daemonsets
  • batch β€” jobs, cronjobs
  • networking.k8s.io β€” ingresses, networkpolicies
  • rbac.authorization.k8s.io β€” roles, rolebindings

When RBAC is fixed but the script still fails

Sometimes fixing the RBAC uncovers a second problem: the binary or script inside the pod can't execute due to wrong Unix file permissions. When that happens, the Unix Permissions Calculator on ToolCraft cuts out the mental overhead of converting between chmod numbers and symbolic notation β€” runs entirely in the browser, nothing leaves your machine.

Related Error Notes