The `secure` runtime class runs each pod in its own lightweight virtual machine, with its own kernel. If a process escapes the container it stays inside that VM, unable to reach the host or a neighbouring pod, even through a kernel-level bug. That is a harder boundary than a [user namespace](/docs/hetzner/apalla/workloads/secure/run-in-a-user-namespace), which remaps UIDs but leaves every pod sharing the one host kernel. Use it when you run untrusted code, mix trust levels in one cluster, or cannot afford a kernel escape. ## Where it runs The `secure` runtime is Kata Containers, and Kata boots a real kernel, so it needs bare-metal nodes with hardware virtualization (KVM). Hetzner Cloud VMs do not expose nested virtualization, so `secure` pods will not schedule on them. No bare-metal workers yet? [Add a bare-metal pool](/docs/hetzner/apalla/servers-and-nodes/provision/add-bare-metal-servers) first. You wire up no node selector; the runtime class carries its own, so the scheduler steers `secure` pods onto capable nodes. When none is free the pod sits `Pending`; it never quietly drops back to the shared-kernel runtime. ## Set the runtime class One line. Add `runtimeClassName: secure` to the pod spec; nothing else about the workload changes. ```yaml title="secure-deployment.yaml" apiVersion: apps/v1 kind: Deployment metadata: name: secure-app spec: replicas: 2 selector: matchLabels: app: secure-app template: metadata: labels: app: secure-app spec: runtimeClassName: secure containers: - name: app image: nginx:stable resources: requests: # size for the container; the VM overhead is added on top cpu: "250m" memory: "128Mi" ``` ## Verify the VM boundary Compare the kernel the pod sees with the node's: ```console $ kubectl exec -- uname -r $ kubectl get node -o jsonpath='{.status.nodeInfo.kernelVersion}' ``` The two versions differ: each `secure` pod runs its own guest kernel inside the VM. A standard-runtime pod reports the node's kernel, because it shares it. ## What a drain does to a secure pod A `secure` pod is still a pod: cordon or drain its node and it evicts and reschedules like any workload. The cost lands only on the way back up: the guest kernel boots again before the container starts, eating into the pool's drain budget. The default `nodeDrainTimeoutSeconds` is 180s; raise it per pool if `secure` pods routinely need longer to hand off. ## Limits to budget for - **Per-pod VM overhead.** The VM layer costs roughly 250m CPU and 160Mi memory on top of the container's request. - **hostNetwork and privileged are out.** A pod that sets either cannot use the `secure` runtime. - **`standard`** (the default) uses crun, the lightweight OCI runtime. A pod with no `runtimeClassName` lands here. - **`runc`** is the fallback for the rare image that misbehaves on crun. - **`secure`** is Kata Containers, the per-pod VM described on this page. Confidential-computing variants (Intel TDX, AMD SEV-SNP) are not built, because Hetzner's dedicated servers lack those CPU features.