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β ServiceAccountdefaultin namespacedefault - What:
createon resourcepods - Where: API group
""(core API group), namespacedefault
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 intoverbsresource "Y"β goes intoresourcesAPI group "Z"β goes intoapiGroups(empty string""= core group)
Common API groups reference
""β core: pods, services, configmaps, secrets, persistentvolumeclaimsappsβ deployments, replicasets, statefulsets, daemonsetsbatchβ jobs, cronjobsnetworking.k8s.ioβ ingresses, networkpoliciesrbac.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.

