## Intro

Runtime Classes in Kubernetes let you select a container runtime for a pod, such as a lightweight sandbox like gVisor, a high-performance runtime like Kata Containers, or a custom runtime with specific features. Backing up and restoring Runtime Class resources is critical for disaster recovery and cluster migration. If a Runtime Class is missing or misconfigured, pods that reference it will fail to schedule, causing outages. This guide covers practical backup and restore procedures with concrete commands, validation steps, and recovery strategies.

You will learn how to:

- Inventory existing Runtime Classes and their configurations.

- Back up Runtime Class objects using standard Kubernetes tools.

- Restore Runtime Classes in a new or recovered cluster.

- Validate that restored Runtime Classes function correctly.

- Handle common failure modes and roll back safely.

This guide targets developers, DevOps engineers, and platform operators who manage Kubernetes clusters and need to ensure runtime configurations survive disasters.

## Version and Environment Inventory

Before backing up Runtime Classes, understand your cluster environment. This section shows how to check the Kubernetes version, confirm the API availability, and list current Runtime Classes.

### Prerequisites

- A running Kubernetes cluster with kubectl configured.

- Access to view Runtime Classes (usually cluster-admin or similar).

### Check Kubernetes Version

RuntimeClass is available in stable v1 since Kubernetes 1.20. Earlier versions used the node.k8s.io/v1beta1 API. Run:

kubectl version --short 
 Expected output (example):

Client Version: v1.27.3
Kustomize Version: v5.0.1
Server Version: v1.27.3 
 If the server version is below 1.20, adjust API versions accordingly. For this guide, we assume v1.

### List Existing Runtime Classes

To see all Runtime Classes:

kubectl get runtimeclass 
 Example output:

NAME HANDLER AGE
runc runc 10d
gvisor runsc 10d
kata kata 10d 
 The HANDLER column shows the runtime handler name configured in the container runtime. It must match the handler in the node's runtime configuration.

### Inspect a Specific Runtime Class

View the full definition of a Runtime Class:

kubectl get runtimeclass gvisor -o yaml 
 Example output:

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
 name: gvisor
 annotations:
 example.com/description: "Sandboxed runtime for untrusted workloads"
handler: runsc
scheduling:
 nodeSelector:
 runtime: gvisor
 tolerations:
 - key: "sandboxed"
 operator: "Equal"
 value: "true"
 effect: "NoSchedule"
overhead:
 podFixed:
 cpu: "100m"
 memory: "128Mi" 
 This Runtime Class specifies a handler, node selector, tolerations, and pod overhead. All these fields need to be backed up.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Quick check question">
Quick check 1 of 2

What is the purpose of a RuntimeClass in Kubernetes?

- To specify the low-level container runtime for a Pod
- To define the network policy for a Pod
- To set resource limits for a container
- To configure persistent storage for a Pod

<button type="button" class="eno-quiz-submit" disabled>Submit</button>
<div class="eno-quiz-explanation">A RuntimeClass allows you to specify the low-level container runtime for a Pod, as described in the reference.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Safe Configuration Path

This section covers the safe way to capture Runtime Class configurations for backup. We use read-only operations first to avoid accidental changes.

### Extract All Runtime Classes to YAML

To back up all Runtime Classes in a namespace-scoped manner (Runtime Classes are cluster-scoped, so no namespace):

kubectl get runtimeclass -o yaml > runtimeclasses-backup.yaml 
 This command captures the full definitions. However, the output includes status and metadata like resourceVersion that are not needed and may cause issues on restore. To get a cleaner backup, use --export (deprecated in newer versions) or process the YAML. In Kubernetes 1.18+, --export is deprecated. Instead, use a tool like kubectl neat or manually remove unwanted fields. For simplicity, we will keep the full output and note that kubectl apply ignores some fields automatically, but resourceVersion should be removed before applying to avoid conflict errors.

To programmatically strip undesired fields, use kubectl get -o json and pipe to jq to remove metadata.resourceVersion , metadata.uid , metadata.creationTimestamp , and status :

kubectl get runtimeclass -o json | jq 'del(.items[].metadata.resourceVersion, .items[].metadata.uid, .items[].metadata.creationTimestamp, .items[].metadata.generation, .items[].status)' > runtimeclasses-clean.json 
 This JSON can be stored as backup.

### Also Back Up Custom Resource Definitions (if any)

If your Runtime Classes reference custom handlers that are defined by extensions, ensure those extensions' configuration is also backed up. For example, if you use a custom runtime installed via a DaemonSet, back up that DaemonSet and related ConfigMaps. Explore using kubectl get daemonset -A -o yaml for runtimes like gVisor that are deployed as DaemonSets.

### Verify Backup File

Check the backup file exists and has content:

wc -l runtimeclasses-clean.json 
 Open it to confirm no empty structures.

## Verification and Diagnostics

After backing up, verify that the backup is complete and correct. Test restoration in a non-production cluster to ensure the process works.

### Create a Test Pod Using the Runtime Class

Before disaster strikes, test that the Runtime Class works. Create a pod that uses gvisor:

# test-gvisor-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: test-gvisor
spec:
 runtimeClassName: gvisor
 containers:
 - name: test
 image: busybox
 command: ["sleep", "3600"] 
 Apply and check status:

kubectl apply -f test-gvisor-pod.yaml
kubectl get pod test-gvisor 
 Expected: status Running. If it fails with FailedCreatePodSandBox or CreatePodSandbox for pod failed , the runtime handler is not available on the node.

### Verify Node Readiness for Runtime

Check nodes have the runtime configured:

kubectl get nodes -l runtime=gvisor 
 If no nodes are returned, the nodeSelector in the Runtime Class does not match any nodes, and pods will not schedule.

### Test Scheduling and Overhead

Create a pod with resource requests to verify overhead is applied:

kubectl apply -f test-gvisor-pod.yaml
kubectl describe pod test-gvisor | grep -A5 "Requests" 
 Expected output shows overhead added to container requests.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Quick check question">
Quick check 2 of 2

Which field in the Pod spec is used to reference a RuntimeClass?

- runtimeClass
- runtimeClassName
- containerRuntime
- runtime

<button type="button" class="eno-quiz-submit" disabled>Submit</button>
<div class="eno-quiz-explanation">The reference passage shows the usage of `runtimeClassName` in a Pod spec.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Failure Modes and Recovery

This section outlines common failure scenarios and how to recover from them.

### Runtime Class Missing During Restore

If you attempt to restore a pod that references a Runtime Class that doesn't exist, the pod will fail to schedule. To recover:

- Check events:

kubectl describe pod mypod | grep -A10 Events 
 Look for FailedScheduling with message "runtimeclass.node.k8s.io/gvisor not found".

- Restore the Runtime Class from backup:

kubectl apply -f runtimeclasses-backup.yaml 

- Recreate the pod if needed.

### Broken Handler Configuration

 If the handler in the Runtime Class does not match any configured runtime on the node, pods will fail to create sandbox. Fix by correcting the handler in the Runtime Class or adding the runtime configuration on nodes. For example, if you mistakenly set handler to runc but nodes only have runsc for that class, update the Runtime Class:

kubectl patch runtimeclass gvisor --type='merge' -p '{"handler":"runsc"}' 
 Then verify pod creation.

### Rollback of Runtime Class Changes

If you change a Runtime Class and it causes issues, revert to a previous version. Keep versioned backups. To restore a specific version:

kubectl apply -f backups/runtimeclass-gvisor-20250301.yaml 
 Use a naming convention with timestamps.

### Cluster-wide Disaster Recovery

In a complete cluster loss, restore Runtime Classes as part of your cluster bootstrap. After cluster creation, apply backups before restoring workloads:

kubectl apply -f runtimeclasses-clean.json 
 Then restore other resources. Ensure order: Runtime Classes before pods/deployments.

## Operations Checklist

Use this checklist to ensure you have a complete backup and restore process:

- [ ] Kubernetes version recorded (kubectl version).

- [ ] All Runtime Classes listed and exported to a version-controlled file.

- [ ] Backup file cleaned of resourceVersion and status fields.

- [ ] Backup stored in off-cluster location (e.g., git, object storage).

- [ ] Test restore process in a staging cluster.

- [ ] Test pod scheduling with each Runtime Class.

- [ ] Verify node labels and tolerations match.

- [ ] Document the mapping of handlers to actual runtimes on nodes.

- [ ] Have a rollback plan for Runtime Class changes.

- [ ] Include Runtime Class backup in regular cluster backup jobs.

Example backup script for cron job:

#!/bin/bash
kubectl get runtimeclass -o json | jq 'del(.items[].metadata.resourceVersion, .items[].metadata.uid, .items[].metadata.creationTimestamp, .items[].metadata.generation, .items[].status)' > /backups/runtimeclasses-$(date +%Y%m%d%H%M).json 

## Conclusion

 Backing up and restoring Kubernetes Runtime Classes is a straightforward but critical task. By following the steps in this guide, you can ensure that your runtime configurations survive disasters and that pods referencing specialized runtimes continue to work. The key points are: inventory your Runtime Classes, back them up cleanly, test restoration thoroughly, and be prepared for common failure modes. Regular backups and validation should be part of your operational routine. Start by implementing the basic backup command and then expand to automated scheduled backups with off-cluster storage.