How to Configure Automatic Node Remediation for DigitalOcean Kubernetes Clusterspublic

Last verified 8 Jul 2026

DigitalOcean Kubernetes (DOKS) is a Kubernetes service with a fully managed control plane, high availability, and autoscaling. DOKS integrates with standard Kubernetes toolchains and DigitalOcean’s load balancers, volumes, CPU and GPU Droplets, API, and CLI.

Note

Automatic node remediation is in public preview. To use it, deploy the custom resources described below.

DigitalOcean Kubernetes (DOKS) can automatically replace worker nodes that stay unhealthy. The Data Plane Operator carries out remediation. It runs in your cluster, watches Node objects, and applies rules you define through cluster-scoped custom resources.

Remediation is opt-in. You enable it for the cluster, define rules for which node conditions trigger a replacement, and monitor remediation through Kubernetes events.

How Automatic Node Remediation Works

When remediation is enabled, the Data Plane Operator watches each worker Node object and, for each one:

  1. Checks whether remediation is enabled cluster-wide in the DataPlaneOperatorConfig object.
  2. Selects the NodeRemediationConfig (NRC) whose nodeSelector best matches the node.
  3. Evaluates the NRC’s spec.unhealthyConditions against the node’s status.conditions.
  4. After the configured grace and duration thresholds pass, issues a remediation action that replaces the worker through the DigitalOcean API.

The operator does not decide what counts as unhealthy for your applications. It reacts to node conditions on each worker: standard kubelet conditions (for example, Ready=False) and custom condition types you define for your environment. You, or software you run in the cluster, set those conditions on the node. The operator replaces the worker when a condition matches your NodeRemediationConfig rules for long enough.

You can set custom conditions with tools such as Node Problem Detector, a DaemonSet or operator that patches node status, or automation tied to your own health checks. The condition type and status strings in your rules must match exactly what you publish on the node.

%%{init: {"themeVariables": {"edgeLabelBackground": "transparent"}} }%%
sequenceDiagram
    participant User as Your CR configuration
    participant DPO as Data Plane Operator
    participant Node as Node (status.conditions)
    participant API as DigitalOcean API
    User ->> DPO: DataPlaneOperatorConfig (enabled)
    User ->> DPO: NodeRemediationConfig (rules)
    Node ->> DPO: Condition updates
    DPO ->> DPO: Match NRC, check duration and budget
    DPO ->> Node: Remediation annotations
    DPO ->> API: cordon-drain-replace or force-replace
    DPO ->> User: Events on NodeRemediationConfig

Prerequisites

Before you configure remediation, make sure you have:

  • kubectl access to the cluster, with permission to create cluster-scoped resources in the dataplane-operator.doks.digitalocean.com API group.
  • Worker nodes that carry the doks.digitalocean.com/node-pool-id label. The operator requires this label to scope replacements to the correct node pool and to observe healthy replacements. DOKS applies it automatically. A node without this label is not remediated.
  • Workloads that tolerate node loss. Remediation replaces the underlying Droplet, so use PodDisruptionBudgets, multiple replicas, and similar safeguards.

API Group and Resources

You configure remediation with two cluster-scoped custom resources in the dataplane-operator.doks.digitalocean.com/v1alpha1 API group:

Resource Scope Purpose
DataPlaneOperatorConfig Cluster Turns cluster-wide remediation on or off.
NodeRemediationConfig Cluster Defines per-policy unhealthy rules, limits, and actions.

In kubectl, refer to them by these names:

  • dataplaneoperatorconfig / dataplaneoperatorconfigs
  • noderemediationconfig / noderemediationconfigs

Apply or update these resources with kubectl apply or your GitOps workflow. The operator reads the live objects from the Kubernetes API. Changes to a NodeRemediationConfig take effect on the next node reconcile.

You can add NodeRemediationConfig objects for different node pools or custom health signals.

Enable Remediation for the Cluster

Remediation is gated for the entire cluster by a single DataPlaneOperatorConfig object named user-config. The operator reads only the object with this exact name and ignores any other name. If user-config does not exist, remediation is disabled until you create the object and set spec.nodeConditionRemediation.enabled to true.

To enable remediation, create the object:

apiVersion: dataplane-operator.doks.digitalocean.com/v1alpha1
kind: DataPlaneOperatorConfig
metadata:
  name: user-config
spec:
  nodeConditionRemediation:
    enabled: true

The spec.nodeConditionRemediation block is optional. When it is absent, remediation behaves as disabled (the same as enabled: false). When it is present, enabled is required:

Field Type Description
enabled boolean When true, the node reconciler can remediate nodes that match a NodeRemediationConfig. When false, the operator updates metrics only and issues no remediation.

Per-policy limits, such as inFlightLimit, observationDeadline, and the unhealthy rules, are set on NodeRemediationConfig, not on this object.

To verify that remediation is enabled, check the object:

kubectl get dataplaneoperatorconfig user-config -o yaml

Even when remediation is disabled, the operator can still export node condition metrics. It issues no replacements until remediation is enabled.

Define Remediation Rules

A NodeRemediationConfig (NRC) is a cluster-scoped policy that defines which node conditions trigger remediation, how long to wait, and what action to take. You can define multiple NRCs. The operator selects one per node, as described in Node Selector Matching.

An NRC supports the following top-level spec fields:

Field Required Type Description
nodeSelector No map[string]string When non-empty, only nodes whose labels match every key/value pair use this config. When empty or omitted, this config acts as a default fallback for nodes that no more specific selector matches.
startupGracePeriod Yes duration string Measured from node.metadata.creationTimestamp, the operator does not remediate the node until this period elapses, even if its conditions look unhealthy. This is a one-time bootstrap safeguard (for kubelet, CNI, and image pulls), not a requirement that the node stay healthy for this long. For example, 10m or 600s. Must be 0s or greater.
inFlightLimit Yes integer, 0 or greater Maximum number of remediations in flight for this NRC at once. 0 disables remediation for nodes that select this config, even when cluster-wide enabled is true.
observationDeadline Yes duration string Maximum time to wait for a healthy replacement before releasing an in-flight slot without observation. This acts as a safety valve. For example, 4h. Must be 0s or greater.
unhealthyConditions No list Rules that map a node condition type and status to a wait duration and remediation action. An empty list means this NRC never triggers remediation.

Unhealthy Condition Rules

Each entry in unhealthyConditions has the following shape:

Field Required Description
type Yes The node condition type (string). Standard kubelet types include Ready, DiskPressure, MemoryPressure, PIDPressure, and NetworkUnavailable. Any type present on node.status.conditions is valid, including custom types you define.
status Yes The condition status to treat as unhealthy: True, False, or Unknown (Kubernetes condition status strings).
duration Yes The minimum time the (type, status) pair must hold continuously on the node before remediation. Measured from condition.lastTransitionTime. For example, 5m or 300s. Must be 0s or greater.
remediationAction Yes The action to take when the rule fires. There is no default. You must set it explicitly.

remediationAction accepts the following values:

Value Behavior
cordon-drain-replace Replaces the worker through the DigitalOcean API with a drain, so workloads are evacuated before the replacement.
force-replace Replaces the worker through the DigitalOcean API without a drain (SkipDrain: true).

There is no alert-only action. Both supported values replace the worker.

The operator checks rules in list order. The first matching unhealthy condition whose duration has elapsed drives remediation.

Built-In Node Conditions

The following are the standard Kubernetes node conditions. Pair the type and status to match your monitoring or alerting model:

Unhealthy signal type status Notes
Not ready Ready False Node not passing ready checks.
Ready unknown Ready Unknown kubelet or contact issues.
Disk pressure DiskPressure True
Memory pressure MemoryPressure True
PID pressure PIDPressure True
Network unavailable NetworkUnavailable True

Custom Node Conditions

Kubernetes allows any condition type string on Node.status.conditions. Use custom types when the built-in signals (Ready, DiskPressure, and so on) are not enough, for example, application-specific health, GPU drivers, or checks only your team understands.

The operator does not create custom conditions for you. You choose the type name, publish the condition on unhealthy nodes, and reference the same type and status in unhealthyConditions. The operator then:

  • Compares each configured (type, status) pair to the node’s current conditions.
  • Waits for the configured duration after lastTransitionTime.
  • Treats a new worker as healthy enough to complete observation when it is Ready=True and does not match any unhealthyConditions entry on the selected NRC.

To add a custom condition:

  1. Choose a unique type string for your organization or workload, for example CustomNodeCondition.
  2. Set that condition on nodes your health system considers failed. The status is usually "True".
  3. Add a matching entry to your NodeRemediationConfig with a duration and a remediationAction.
  4. Confirm the condition appears on a test node with kubectl describe node <node-name>, under Conditions.

The following NRC fragment matches a custom condition:

unhealthyConditions:
  - type: CustomNodeCondition
    status: "True"
    duration: 10m
    remediationAction: cordon-drain-replace

Use the same type and status strings everywhere: in the detector, the NRC, and your runbooks.

Node Selector Matching

Only one NodeRemediationConfig applies to a given node at a time. When more than one NRC could apply to a node based on its nodeSelector, the operator picks the best match using this precedence:

  1. The config with the largest number of matching selector keys wins.
  2. If no selector matches, the operator uses an NRC with an empty nodeSelector ({} or omitted) as the default, if one exists.
  3. If no selector matches and there is no default, the node is not remediated under any NRC.

In the following example, a pool-specific config overrides the default:

# Default for all nodes
apiVersion: dataplane-operator.doks.digitalocean.com/v1alpha1
kind: NodeRemediationConfig
metadata:
  name: default
spec:
  startupGracePeriod: 15m
  inFlightLimit: 3
  observationDeadline: 4h
  nodeSelector: {}
  unhealthyConditions:
    - type: Ready
      status: "False"
      duration: 10m
      remediationAction: cordon-drain-replace
---
# Stricter policy for a GPU pool
apiVersion: dataplane-operator.doks.digitalocean.com/v1alpha1
kind: NodeRemediationConfig
metadata:
  name: gpu-pool
spec:
  startupGracePeriod: 20m
  inFlightLimit: 1
  observationDeadline: 4h
  nodeSelector:
    doks.digitalocean.com/node-pool: gpu-workers
  unhealthyConditions:
    - type: CustomGPUHealthSignal
      status: "True"
      duration: 5m
      remediationAction: cordon-drain-replace

We recommend a longer startup grace period for large GPU nodes, which can take longer to initialize.

Disable Remediation for a Subset of Nodes

To disable remediation for a group of nodes, set inFlightLimit: 0 on the NRC that those nodes select. A cluster-wide enabled: true does not override this.

Remediation Lifecycle

Preconditions

The operator remediates a node only when all of the following hold:

  1. DataPlaneOperatorConfig/user-config has spec.nodeConditionRemediation.enabled: true.
  2. A matching NRC exists with an inFlightLimit greater than 0.
  3. The node is past its startupGracePeriod, measured from creation.
  4. An unhealthyConditions rule matches and its duration has elapsed since lastTransitionTime.
  5. The node is not already marked with a remediation-issued annotation.
  6. The node has the doks.digitalocean.com/node-pool-id label, which is required for pool-scoped replacement and observation.

Remediation Steps

When the preconditions hold, the operator:

  1. Reserves an in-flight slot on the matched NRC.
  2. Sets remediation annotations on the node.
  3. Calls the DigitalOcean API to replace the worker (cordon-drain-replace or force-replace).
  4. Emits a RemediationIssued event on the NRC.
  5. Releases the slot and emits a RemediationObserved event when a healthy replacement appears in the same node pool.

In-Flight Remediation Budget

Each NRC has its own cap, set by inFlightLimit. A remediation is in flight from the moment it is issued until one of the following happens:

  • A healthy replacement is observed in the same doks.digitalocean.com/node-pool-id (a RemediationObserved event).
  • The observationDeadline elapses without observation (a RemediationUnobserved event).

When the budget is full, the operator does not issue another remediation for that NRC. It rechecks about once a minute and starts a new remediation when a slot frees up.

Monitor Remediation with Events

The operator records Kubernetes events on the NodeRemediationConfig that applied to the node, not on the Node object. Events are the primary way to see what happened during remediation.

To view events for an NRC:

kubectl describe noderemediationconfig <nrc-name>

To list remediation events across all NRCs:

kubectl get events --field-selector involvedObject.kind=NodeRemediationConfig

The operator emits the following events:

Reason Type When Meaning
RemediationIssued Normal After a slot is reserved and the replace API is called. Remediation started for a node. The message includes the action, node name, and pool ID.
RemediationObserved Normal A healthy replacement is observed. The in-flight slot is released and the budget is freed.
RemediationUnobserved Warning The observationDeadline is exceeded. The slot is released without seeing a healthy replacement.
RemediationFailed Warning The replace API or a prior step failed. The slot and remediation annotations are rolled back, and remediation is retried.
InFlightLimitReached Warning The budget became full while blocking a node. This NRC’s in-flight cap is reached, and node remediation is requeued.
InFlightLimitCleared Normal The budget went from full to not full. Capacity is available again on this NRC.
MissingNodePoolLabel Warning The node lacks the doks.digitalocean.com/node-pool-id label. Remediation is refused for that node.

A RemediationIssued message looks like this:

Issued cordon-drain-replace on node "worker-pool-abc123" (pool "uuid-...").

A RemediationObserved message looks like this:

Observed healthy replacement for node "worker-pool-abc123" in pool "uuid-..." (action cordon-drain-replace issued at 2026-05-29T12:00:00Z).

Full Configuration Example

The following manifest enables remediation for the cluster and defines a default NRC that replaces nodes reporting a custom unhealthy signal:

apiVersion: dataplane-operator.doks.digitalocean.com/v1alpha1
kind: DataPlaneOperatorConfig
metadata:
  name: user-config
spec:
  nodeConditionRemediation:
    enabled: true
---
apiVersion: dataplane-operator.doks.digitalocean.com/v1alpha1
kind: NodeRemediationConfig
metadata:
  name: default
spec:
  startupGracePeriod: 10m
  inFlightLimit: 3
  observationDeadline: 4h
  nodeSelector: {}
  unhealthyConditions:
    - type: CustomNodeSignal
      status: "True"
      duration: 10m
      remediationAction: cordon-drain-replace

Troubleshooting

Symptom What to check
No remediation Confirm DataPlaneOperatorConfig/user-config has enabled: true, a matching NRC exists, inFlightLimit is greater than 0, the node is past its startupGracePeriod, the condition duration has elapsed, and the node is not already annotated with remediation-action-issued-at.
Wrong policy applied Review selector precedence when multiple NRCs exist. Check node labels with kubectl get nodes --show-labels.
Stuck in flight Run kubectl describe noderemediationconfig <nrc-name> and look for RemediationUnobserved or RemediationObserved events. Check the status ConfigMap entries and the observationDeadline.
Custom condition ignored Confirm the type and status strings match exactly, the condition is present on the node (kubectl describe node <node-name>), and lastTransitionTime is old enough for the configured duration.
Remediation refused Look for a MissingNodePoolLabel event and confirm the node has the doks.digitalocean.com/node-pool-id label.
Node never remediated again Remediation annotations are still present. Remove them manually to allow a re-run on the same object name.

We can't find any results for your search.

Try using different keywords or simplifying your search terms.