Troubleshooting
This document provides guidance for diagnosing issues with runtime-enforcer.
Enable verbose logs
To enable more verbose logs in each container via Helm values:
helm upgrade --install runtime-enforcer runtime-enforcer/runtime-enforcer \
--namespace runtime-enforcer \
--set agent.logLevel=debug \
--set controller.logLevel=debug \
--reuse-values
WorkloadPolicy status failures
Each WorkloadPolicy reports its current deployment status on all targeted nodes
in its .status field. Run:
kubectl describe workloadpolicy <name> -n <namespace>
Look for the Status section. It shows the overall phase and a breakdown of
nodes:
Status:
Failed Nodes: 1
Nodes With Issues:
worker-3:
Code: PolicyFailed
Message: failed to populate policy for wp default/allow-shell, container app: ...
Observed Generation: 1
Phase: Failed
Successful Nodes: 2
Total Nodes: 3
The Phase field can be one of:
-
Ready- the policy is enforced on all targeted nodes -
Failed- at least one node has a problem -
Transitioning- some nodes are still switching enforcement mode
The node counters (SuccessfulNodes, FailedNodes, TransitioningNodes)
always add up to TotalNodes.
Node issue codes
When a node has a problem, it appears in Nodes With Issues with a Code
field. The codes are:
-
PodNotReadyThe controller cannot reach the agent pod on this node. The gRPC connection failed or the agent pod is not running. Check if the agent DaemonSet has a pod scheduled on that node and whether it is inRunningstate.kubectl get pods -n runtime-enforcer --field-selector spec.nodeName=<node-name> kubectl describe pod -n runtime-enforcer -l app.kubernetes.io/component=agent --field-selector spec.nodeName=<node-name>If the agent pod is crash-looping, inspect its logs:
kubectl logs -n runtime-enforcer -l app.kubernetes.io/component=agent --field-selector spec.nodeName=<node-name> -
MissingPolicyThe agent pod is reachable but the policy is not loaded on it. This typically happens when the policy was just created and the agent on that node has not reconciled yet, or the pod carrying the policy label was not scheduled on that node. If this persists, check the agent logs for reconciliation errors. -
PolicyFailedThe agent received the policy but failed to apply it. TheMessagefield contains the underlying error, usually a BPF map update failure or an internal resolver error. This means something went wrong inside the agent on that specific node. Check the agent logs on that node for more context:kubectl logs -n runtime-enforcer -l app.kubernetes.io/component=agent --field-selector spec.nodeName=<node-name>Look for log lines containing
reconcile wp-policyfollowed by an error. The message in the WorkloadPolicy status is a copy of that error.
Controller-side diagnostics
If the Nodes With Issues section is empty but the phase is still Failed, the
status sync controller itself may have encountered an error while collecting
node data. The controller runs on a configurable interval and logs every sync
attempt. Check its output:
kubectl logs -n runtime-enforcer -l app.kubernetes.io/component=controller
Look for lines from the WorkloadPolicyStatusSync logger. Errors at this level
indicate problems listing WorkloadPolicies, connecting to agents, or updating
the status subresource.
Quick overview with the kubectl plugin
The kubectl runtime-enforcer policy show protection command lists all
workloads, their associated policies, enforcement mode, and current status phase
in one table. This is useful when you have multiple policies and want to see
which ones aren’t Ready.
kubectl runtime-enforcer policy show protection
Output:
WORKLOAD KIND POLICY MODE STATUS default/my-app Deployment allow-shell Protect Ready default/legacy-app Deployment restrict-exec Monitor Failed kube-system/coredns Deployment coredns-policy Protect Ready
Use the --all-namespaces flag to include policies from every namespace, and
-o json for machine-readable output.
Debugger
The debugger is an optional Kubernetes Deployment that helps diagnose issues between the runtime-enforcer agents and the actual state of the Kubernetes cluster.
At the moment, it supports only a few basic operations listed below.
Features
Cache comparison
Each agent maintains an in-memory cache of the pods and containers it is aware of on its node. This cache is populated via the NRI (Node Resource Interface) plugin and is used to resolve container identities during policy enforcement.
If the agent cache drifts from the real cluster state, enforcement decisions could be applied incorrectly so it’s important to validate if the cache is in sync.
The debugger periodically:
-
Queries every agent (via mTLS-secured gRPC) for its current pod/container cache.
-
Lists all pods from the Kubernetes API.
-
Compares the two views node-by-node and prints a diff to stdout.
If the caches are aligned you’ll see:
caches are aligned
If there is a discrepancy you’ll see a diff with the affected node and pods, followed by a full dump of the agent cache for that node.
NRI timeouts and required plugins
Runtime Enforcer relies on NRI (Node Resource Interface) integration provided by the container runtime.
Some environments may hit the NRI default request timeout (5s to register a
plugin and 2s to handle an event), which can cause the NRI plugin to be
detached from the container runtime. This can cause the agent to repeatedly
retry connecting to the NRI plugin, resulting in the agent not coming up as
expected.
When this happens, you will see the runtime-enforcer agent crashing with the following information in its logs:
{"time":"2026-04-24T15:36:07.617127725Z","level":"INFO","msg":"ttrpc server closed 00-runtime-enforcer-agent : ttrpc: server closed","component":"agent","component":"nri-handler","component":"nri-stub"}
You’ll also see logs like the following in containerd’s logs, indicating that the configured timeout value was exceeded:
Apr 24 15:36:27 kind-control-plane containerd[114]: time="2026-04-24T15:36:27.664534740Z" level=info msg="connection to plugin \"00-runtime-enforcer-agent\" closed" Apr 24 15:36:27 kind-control-plane containerd[114]: time="2026-04-24T15:36:27.664575617Z" level=info msg="failed to synchronize plugin: context deadline exceeded" Apr 24 15:39:38 kind-control-plane containerd[114]: time="2026-04-24T15:39:38.782163498Z" level=error msg="closing plugin 00-runtime-enforcer-agent, failed to handle event 6: context deadline exceeded"
To mitigate this, you can increase the NRI timeout on the container runtime side by editing the container runtime configuration file.
containerd: increase NRI timeout
Edit /etc/containerd/config.toml and update the NRI plugin timeouts under
[plugins."io.containerd.nri.v1.nri"]:
The example below changes the settings to 10s and 5s.
[plugins."io.containerd.nri.v1.nri"]
# plugin_registration_timeout is the timeout for a plugin to register after connection.
plugin_registration_timeout = "10s"
# plugin_request_timeout is the timeout for a plugin to handle an event/request.
plugin_request_timeout = "5s"
For details and additional NRI settings, see containerd NRI configuration documentation.
CRI-O: increase NRI timeout
Edit /etc/crio/crio.conf and update the crio.nri table:
The example below changes the setting to 10s and 5s.
[crio.nri]
# Timeout for a plugin to register itself with NRI.
nri_plugin_registration_timeout = "10s"
# Timeout for a plugin to handle an NRI request.
nri_plugin_request_timeout = "5s"
For details, see CRI-O NRI configuration documentation.
Required Plugins
Additionally, recent container runtimes provide an option to require a set of NRI plugins to always be present for container creation to succeed.
Follow the steps below to configure this on a per-workload basis. Check NRI docs for more information about the best option to suit your environment.
Workloads
For workloads that you want to ensure have runtime-enforcer protection present,
add the required-plugins.noderesource.dev annotation to its pod spec:
apiVersion: apps/v1
kind: Deployment
metadata:
name: opensuse-deployment
labels:
app: opensuse
spec:
replicas: 1
selector:
matchLabels:
app: opensuse
template:
metadata:
labels:
app: opensuse
security.rancher.io/policy: workloadpolicy-sample
annotations:
# This specifies "runtime-enforcer-agent" plugin to be required.
required-plugins.noderesource.dev: '["runtime-enforcer-agent"]'
spec:
containers:
- name: opensuse
image: registry.opensuse.org/opensuse/bci/bci-ci:3
stdin: true
tty: true
When the required plugin isn’t available for any reason, the workload fails to start with errors in Kubernetes events:
4s Warning Failed pod/opensuse-deployment-6655f5c7ff-xghnd Error: failed to create containerd container: failed to get NRI adjustment for container: validator "00-default-validator" rejected container adjustment, reason: validation error: required plugin "runtime-enforcer-agent" not present
Enabling the debugger
The debugger is disabled by default. Enable it via Helm values:
helm upgrade --install runtime-enforcer runtime-enforcer/runtime-enforcer \
--namespace runtime-enforcer \
--set debugger.enabled=true \
--reuse-values
|
|
Tocustomize how often the debugger compares the agent cache against the cluster
state, you can set the debugger.interval value in your Helm command:
--set debugger.interval=<interval> # e.g. 1m, 30s