Section 01
There is no restore verb
The simplest possible check, and the most conclusive one.
$ grep -in "restore" pkg/kubelet/server/server.go
(no matches)
$ grep -n "checkpoint" pkg/kubelet/server/server.go
117: checkpointPath = "/checkpoint/"
692: // Only enable checkpoint API if the feature is enabled
1278:func (s *Server) checkpoint(request *restful.Request, response *restful.Response) {
One direction is an endpoint. The other is not represented in the kubelet’s HTTP surface at all. This is not an oversight — it follows from the feature’s stated purpose. KEP-2008 is titled Forensic Container Checkpointing; the goal was to obtain a copy for analysis, not to resume execution.
Section 02
What master adds — and what it does not
The CRI is moving. It is worth being precise about what exists where, because it is easy to read a proto file on main and describe a capability no released cluster has.
release-1.36 master CheckpointContainer 1 1 CheckpointPod 0 1 RestorePod 0 1
So a RestorePod RPC is arriving — pod-level checkpoint/restore work, with its own proto comment describing a freeze-all / resume-all contract and requiring restored containers to be “in the CREATED state and must not have executed the restored process”. Two cautions:
- It is on master, not in the release tested here.
- It is a CRI RPC — an interface between kubelet and runtime. It is not a kubelet HTTP endpoint, and the kubelet server still contains no
restore.
“Kubernetes can restore containers” is false on 1.36 as an API statement and misleading even on master. What is true: CRI-O can restore a container, and you reach that capability by constructing an image, not by calling Kubernetes.
Section 03
The procedure that stands in for the verb
Three steps, all userland. The archive is wrapped in a scratch image carrying one annotation, and a pod references that image like any other.
Section 04
The blocker nobody mentions
With the image built correctly, the restored pod would not start:
Status: CreateContainerError Warning Failed kubelet Error: namespaced signature policy /etc/crio/policy.json defined for pods in namespace default; signature validation is not supported for container restore
// WARNING: This hard-codes an assumption that SignaturePolicyPath set specifically for the
// namespace is never less restrictive than the default system-wide policy, i.e. that if an
// image is successfully pulled, it always conforms to the system-wide policy.
if systemCtx.SignaturePolicyPath != "" {
return "", fmt.Errorf(
"namespaced signature policy %s defined for pods in namespace %s; signature validation is not supported for container restore", ...)
CRI-O refuses to restore whenever a signature policy path resolves for the pod’s namespace — and the CRI-O static bundle installs one at /etc/crio/policy.json by default. So a stock install can take checkpoints but cannot restore them until that is cleared. Setting signature_policy = "" made the restore succeed.
Note what that trade is: restoring a checkpoint image bypasses image signature verification, by design, because the archive is not a signed artifact. In a cluster that enforces image signing, this path is a hole worth knowing about before you open it.
Section 05
It really does resume
The test that settles whether this is a restore or a restart: a container incrementing a counter, checkpointed mid-count.
counter value before checkpoint : 1158 archive : checkpoint-counter_default-app-2026-09-13T17:57:46Z.tar restored pod status : Running counter value in restored pod : 1182 (~24 s later — it continued)
A restarted container would have begun at zero. The counter carried on from where it was, which means the process’s memory came back. Chapter 04 pushes this much harder — child processes, descriptors, sockets — and finds where it stops.
An early version of this test concluded “the process restarted” because the restored container’s log began with the original boot banner. It does — CRI-O restores the container log along with the process, so the archive’s log is replayed before execution continues. The reliable signal is that there is exactly one boot banner and the counter never resets, not that the log looks like a fresh start.
Section 06
What it costs
alloc rss_kB archive_B ratio ms
0M 2908 956416 .32 97
16M 19528 17915904 .89 119
64M 68776 68353536 .97 180
256M 265552 269883392 .99 554
For a memory-heavy container the archive is essentially the size of its resident memory. The 0.32× at the small end is not a compression win — it is that most of a tiny process’s RSS is shared, file-backed pages (the binary and libraries) which CRIU references rather than copies. Anonymous memory is copied in full.
Time scales with it too: roughly 2 ms per 10 MiB of anonymous memory on this hardware, on top of a ~90 ms floor. A container with 8 GiB resident should be expected to produce an 8 GiB file and to be frozen for the duration — plan node disk accordingly, because the archive lands on the node.
Section 07
Further reading
Section 08
Closing note — what varies, and what was not verified
Durable versus run-specific
- Durable: no
restorein the kubelet server; the 1.36-versus-master RPC difference; the signature-policy refusal and its source line; that the counter continues rather than resets; that the ratio approaches 1.0× as anonymous memory grows. - Run-specific: 1158 and 1182; every millisecond figure; the exact byte counts. Timings are from a QEMU guest on Apple silicon and will differ substantially on server hardware.
A failure worth recording
A workload image built FROM scratch failed to restore on this stack, with CRIU reporting mnt: No mapping for 1445:(null) mountpoint and the container then starting fresh. The same experiment on a busybox-based image restored correctly. The restore path appears sensitive to the shape of the container’s root filesystem. That was not root-caused here — it is reported as observed, and it is why every measurement in this chapter uses a conventional base image.
Not verified
- Restore onto a different node. Everything here is single-node. Cross-node restore requires moving the archive and having a compatible kernel and runtime at the far end; not tested.
- CheckpointPod / RestorePod. Not in 1.36; nothing about them was executed.
- Restore with volumes. The subject had no PersistentVolume. Interaction between a restored process and reattached storage was not examined.
- Signature enforcement. It was switched off to proceed; no test was made of restoring under a policy that permits the image.
Section 09
Spoken drills
Does Kubernetes support restoring a checkpointed container?
A strong answer hits
- Not as an API — the word
restoredoes not appear in the kubelet HTTP server on any version checked - The path back is userland: wrap the archive in an OCI image, run a pod from it
- CRI-O does the restoring; Kubernetes just starts a pod
- A
RestorePodCRI RPC is appearing on master, but that is runtime-facing and not in 1.36 - The honest half: it genuinely works — the process resumes mid-execution. The absence is of a verb, not of the capability
check against §01, §02 and §05
How much disk and how much pause should you budget for checkpointing a 4 GiB service?
A strong answer hits
- Roughly 1× resident memory — the ratio measured 0.99 at 256 MiB and is still climbing
- The archive lands on the node, so it is node disk, not cluster storage
- Pause scales with memory: ~2 ms per 10 MiB here, plus a ~90 ms floor
- The honest half: the ratio is low for small processes because shared file-backed pages are referenced, not copied — so you cannot extrapolate down from a big number or up from a small one
check against §06
Your restore fails with CreateContainerError mentioning signature policy. What is happening, and what are you accepting by fixing it?
A strong answer hits
- CRI-O refuses restore when a namespaced signature policy path resolves
- The static bundle installs one by default, so a stock node hits this
- Clearing it lets the restore proceed
- The honest half: you are accepting that checkpoint images bypass signature verification. In a cluster that enforces signing, this is a real gap — the archive is not a signed artifact and cannot be made into one this way
check against §04
A restored pod’s log starts with the application’s boot banner. Did the restore fail?
A strong answer hits
- Not necessarily — CRI-O restores the container log too, so the pre-checkpoint log is replayed
- The signal is whether there is one boot banner or two, and whether counters reset
- Measured: one banner, counter continuing 1158 to 1182
- The honest half: this genuinely produced a wrong conclusion during this work. If you are testing restore, instrument something monotonic in memory rather than reading the log’s first line
check against §05