The API said no
Two different mechanisms refuse things, and they look different. Read the first line of the error and you know which one you are dealing with.
Forbiddenmeans RBAC: you are not allowed to touch that kind of object at all.denied the requestmeans a platform rule: your YAML is fine, but what it asks for is not permitted.
The second one matters because there is nothing to fix in the manifest. You need a different approach, and the message says which rule fired.
Forbidden on a kubectl command
Three causes, in the order they actually happen.
Your default namespace is not set. The command ran against default, which you have no access to. Check what the current context carries:
kubectl config view --minify | grep namespaceIf it says nothing, set it once, see Access and kubectl.
The resource is not one you may touch. Your namespace grants a specific list, and everything else is denied. Ask directly rather than guessing:
kubectl auth can-i create scaledobjects
kubectl auth can-i list nodesThe full list is under Permissions and policies. Some grants are read-only, notably your quota and limit ranges, so get can succeed while edit is refused on the same object.
Cluster-scoped reads are always refused, including harmless-looking ones:
Error from server (Forbidden): nodes is forbidden: User "..." cannot list
resource "nodes" in API group "" at the cluster scopeThat is intended, not a broken setup. It also means you cannot run kubectl get storageclass; the two class names are on Storage.
KUBECONFIG points somewhere else. Confirm where you are:
kubectl config current-contextdenied the request on apply
A platform rule fired. The shape is always the same:
Error from server: error when creating "app.yaml": admission webhook denied the
request: <the rule that fired, and why>The full list of rules is under Permissions and policies. The ones that come up most often:
| The message mentions | What it means | What to do |
|---|---|---|
| Volume type not allowed | An inline volume in the pod spec | Ask for storage with a PersistentVolumeClaim |
| Only ClusterIP Services are permitted | NodePort, LoadBalancer or ExternalName | Stay on ClusterIP and publish with an HTTPRoute |
| may not create Certificate | A Certificate you wrote yourself | Let the annotation on your HTTPRoute issue it |
| Hostname is not allowed in namespace | A hostname your namespace may not serve | See below |
| must declare at least one hostname | An HTTPRoute with no hostnames | Add one |
| Free-tier namespaces may only use the nfs-rwx StorageClass | Block storage on the free tier | Use nfs-rwx, see Free tier |
| may not tolerate | A toleration that matches everything | Remove it and let the scheduler place the pod |
| may not set spec.nodeName | A hand-picked node | Remove it |
| body.maxSize | A request body limit above the cap, or zero | Set an explicit size at or below 512 MiB |
Hostname is not allowed in namespace
Hostname is not allowed in namespace 'tenant-7'. Tenant namespaces may only use
*.tenant-7.itsh.dev (paid) or *.tenant-7.itsh-apps.dev (free) subdomains or
hostnames listed in the itsh.dev/allowed-hostnames namespace annotation.Your namespace owns one subdomain and can serve any name under it immediately. Anything else has to be added in the portal first, where you prove the domain is yours with a DNS record.
Free namespaces cannot use a custom domain at all. The full rules are on Gateway API.
Exec/attach is only allowed into gVisor-sandboxed pods
You tried to get a shell in a managed PostgreSQL pod. Those run outside the sandbox, and exec is only permitted into sandboxed pods, so this combination is refused.
It applies to PostgreSQL only. MariaDB and Dragonfly pods take a shell like any other. To run SQL, forward the port and use a local client, which is not affected. See Databases.
What's next
- Permissions and policies for the complete list
- A pod will not start if the manifest applied but nothing came up