Skip to content

Cluster Policies

Kupe Cloud applies workload policies to keep managed clusters secure and predictable. This page focuses on the rules tenants most often need to account for, the defaults Kupe applies for you, and the common fixes when a deployment is rejected.

PolicyModeWhat it means for you
No privileged or host-level accessEnforcePrivileged containers, host networking, host PID/IPC, and hostPath volumes are rejected
Non-root containersEnforcerunAsNonRoot: true and allowPrivilegeEscalation: false are required
Drop all capabilitiesEnforcecapabilities.drop: ["ALL"] is required unless you use the capability exemption path below
Resource requests requiredEnforceCPU and memory requests are required; defaults are applied if you omit them
Default service account token mountAutomaticThe default ServiceAccount does not mount a token unless you opt in
No NodePort / LoadBalancer servicesEnforceUse ClusterIP plus an HTTPRoute for external access
HTTPRoute hostname validationEnforcePublic route hostnames must stay within your tenant’s allowed domain space
Seccomp profileAuditRuntimeDefault or Localhost is recommended but not yet required
Read-only root filesystemAuditreadOnlyRootFilesystem: true is recommended but not yet required
Image registry policyAuditRegistry usage is logged for review; deploys are not blocked

If your pod omits resources.requests or resources.limits, Kupe applies sensible defaults before policy validation runs. That means a simple workload is not rejected just because you forgot to set resource fields.

The defaults are:

FieldDefault value
resources.requests.cpu100m
resources.requests.memory128Mi
resources.limits.cpuSame as requests (auto-set)
resources.limits.memorySame as requests (auto-set)

How limits are set: if you omit limits, the platform automatically sets them equal to your requests — giving your pod Guaranteed QoS (the highest priority class in Kubernetes). This means your pod won’t burst above its request, but it also won’t be evicted before Burstable pods during node pressure.

If you want burst headroom (e.g., a web server that idles at 100m but spikes to 400m during request peaks), set both requests and limits explicitly:

resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 400m, memory: 512Mi }

Per-pod limit ratios are capped: limits can be at most 4× requests for CPU and 2× requests for memory. This prevents “request 50m, limit 100 CPU” abuse while giving you meaningful burst room for real workloads.

These defaults only apply to containers that don’t set their own resources. If you declare requests, that’s what runs — limits default to match your requests. If you declare both, your values are used as-is (subject to the ratio cap).

Billing is on actual usage, not requests. You pay for the CPU and memory your workloads actually consume, not what they reserve. Setting higher requests reserves more scheduling capacity (and counts against your cluster’s quota) but doesn’t increase your bill unless your workload actually uses it. See Cluster resources and quotas for how quota and billing interact.

Some workloads legitimately need a Linux capability that the default drop: ["ALL"] policy would block:

  • Redis with maxmemory + maxmemory-policy using mlock → IPC_LOCK
  • Profilers / debuggers (gdb, strace, pprof sidecars) → SYS_PTRACE
  • VPN sidecars (wireguard, tailscale) → NET_ADMIN, NET_RAW

To allow these without opening a support ticket, add a pod-level annotation explaining why you need the exemption:

apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
template:
metadata:
annotations:
# Required. Explain why the extra capability is needed.
kupe.cloud/capability-exemption: "Redis maxmemory mlock - IPC_LOCK needed"
spec:
containers:
- name: redis
image: redis:7
securityContext:
capabilities:
drop: ["ALL"]
add: ["IPC_LOCK"]

Important rules:

  1. The annotation is required to be non-empty. An empty string does not grant exemption.
  2. Keep the add list minimal. The exemption mechanism turns off the drop: ["ALL"] check entirely, so only add the capabilities you actually need.
  3. Use exemptions sparingly. Most workloads do not need extra capabilities.
  4. Other policies still apply. The exemption only affects the Drop all capabilities rule. Enforced controls such as runAsNonRoot and allowPrivilegeEscalation still reject violating workloads, while audit-only checks remain informational.

If you’re not sure whether you need an exemption, try without it first — most modern images work cleanly with drop: ["ALL"] and the default NET_BIND_SERVICE add-back for binding ports below 1024.

When a policy blocks your deployment, the error message Kupe returns includes the exact YAML to add. This section collects the most common ones for reference.

”Tenant containers must set runAsNonRoot: true”

Section titled “”Tenant containers must set runAsNonRoot: true””

Add a securityContext block to your pod spec:

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65532 # nobody
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: ghcr.io/your-org/app:v1.0.0
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]

If your image only runs as root, the fastest fix is usually to switch to a non-root base image (Chainguard, Red Hat UBI, distroless, or Bitnami’s nonroot variants). Most official images on Docker Hub (postgres, redis, nginx, mysql, mariadb) support non-root out of the box.

”Containers must drop ALL capabilities”

Section titled “”Containers must drop ALL capabilities””

Two options:

Option 1 — drop everything (the happy path):

containers:
- name: app
securityContext:
capabilities:
drop: ["ALL"]

Option 2 — drop all and add specific ones back with an exemption:

See Capability exemptions above.

You rarely need to set these yourself — Kupe applies sensible defaults for any container that omits them. If you’re hitting this error anyway, you probably set one field (e.g. requests.cpu) but not another — fill in the missing pair:

containers:
- name: app
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi

“NodePort and LoadBalancer services are not allowed”

Section titled ““NodePort and LoadBalancer services are not allowed””

Change your Service to ClusterIP and create an HTTPRoute for external access:

apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
type: ClusterIP # ← was NodePort or LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080

Then follow the standard route pattern in HTTP Routes to publish the service.

If you see a registry policy warning or error

Section titled “If you see a registry policy warning or error”

On the standard Kupe tenant policy set, the registry policy is currently in audit mode. Registry usage is logged, but deploys are not blocked. If your cluster is using a stricter custom policy and a deploy is blocked, contact support.

Some policies run in audit mode — violations are logged to the platform but don’t block the deploy. This gives you visibility before a rule becomes a hard requirement.

Currently in audit mode:

  • Seccomp profile requirement — most modern runtimes set RuntimeDefault automatically; audit mode helps catch workloads that still need adjustment.
  • Read-only root filesystem — breaks many common images that write to /tmp or /var/log; audit lets you migrate at your own pace.
  • Image registry policy — currently informational; deploys continue while registry usage is recorded for visibility.

These may be promoted to enforce mode in a future update with advance notice.