Skip to content

Implement Snapshot/Restore API for workload pods in Kata - #467

Open
michaelxu2288 wants to merge 8 commits into
msft-mainfrom
michaelx/up/3.32.0/snapshot-restore-feature
Open

Implement Snapshot/Restore API for workload pods in Kata#467
michaelxu2288 wants to merge 8 commits into
msft-mainfrom
michaelx/up/3.32.0/snapshot-restore-feature

Conversation

@michaelxu2288

@michaelxu2288 michaelxu2288 commented Jul 27, 2026

Copy link
Copy Markdown

This PR adds snapshot and restore API for Kata workload pods.

A snapshot captures a running pod's VM to disk. Running kata-runtime snapshot against a live Cloud Hypervisor sandbox writes the guest memory into a chosen snapshot directory and resumes it, so the source pod only stops for the duration of the save. The snapshot artifact holds the entire guest state which lets the same process be brought back later on another pod using COW.

Restore is driven entirely from a pod annotation. Adding

io.katacontainers.restore-from

to a normal pod spec routes RunPodSandbox through the custom restore path in this PR instead of a cold boot, so the restored clone comes up as an ordinary containerd-managed, CNI-networked pod. The VM boots from the snapshot's COW, which leaves the snapshot file untouched and lets several clones share one snapshot, and the frozen process resumes exactly where it left off. the VM comes back paused and only resumes once the workload reaches StartContainer.

Because the clone must not answer on the source pod's IP, restore gives it a fresh network identity. the VM keeps the single guest NIC from the snapshot and rebinds it to the new pod's CNI tap through Cloud Hypervisor's vm.restore net_fds path, which passes the host-side tap file descriptors and leaves the guest device and the CLH binary unchanged. After resume, the guest NIC is reprogrammed to the target CNI's MAC, IP, and then read back to confirm the change landed before any traffic is allowed.

The entire restore path is fail-only which means any inconsistency during restore aborts and reaps the VM immediately and throws a failure message.

Camelron and others added 8 commits July 27, 2026 18:14
…eparation

In its current form, the only use of Snapshot/Restore api for
a given hypervisor is for VM templating, configured via the
BootToBeTemplate and BootFromTemplate configs.

Replace these hypervisor-level configs with a new
FileBackedMemory abstraction so that other usecases for
SaveVM/RestoreVM can use it.
…andbox)

Signed-off-by: Michael Xu <t-michaelxu@microsoft.com>
`kata-runtime restore --from <snapshot>` (plus the shim's restore mode)
rebuilds a Sandbox from a snapshot dir, boots the VM from its saved memory
(copy-on-write), adopts the live agent + hypervisor, and runs it as a real
kata-managed sandbox (own shim, lifecycle, management API). Proves the guest
RAM survives a snapshot; the clone is not yet a networked pod.
…ainerd and CNI

Drive restore from a pod annotation (`restore_from`) instead of the hand-run
CLI: RunPodSandbox dispatches to RestoreSandbox, and the restored clone comes
up as a real containerd-managed, CNI-networked, isolated pod. Adopt the pod's
CNI netns, hotplug the CNI NIC, re-IP the guest NIC by MAC, install routes,
and neutralize the frozen snapshot NIC so the clone shares no network identity
with its source.
Restore a Kata snapshot as a real, networked, isolated Kubernetes pod through the
`io.katacontainers.restore-from` pod annotation on the normal CRI path.

The restored VM keeps its single guest NIC and rebinds it to the fresh CNI tap via
CLH `vm.restore` net_fds (no second NIC, no ghost neutralization, no CLH change).
The VM restores paused and resumes at workload StartContainer; the sole workload is
adopted host-only (no re-create in the guest); the guest NIC identity is replaced to
the exact target CNI MAC/IP/routes and verified before a TAP-up/no-redirect fence is
activated. Fail-only: any restore anomaly aborts and reaps the VM.

Squash of the net_fds refactor; drops the standalone restore CLI launcher and the
perf/timing instrumentation.
A restored sandbox reconnects to an already-live snapshotted agent, so the
first hybrid-vsock trivial handshake reliably hits the known kernel vsock
race where the agent's first reply is dropped. The dialer then waits the full
hardcoded 10s handshakeTimeout before commonDialer redials (which succeeds
instantly), making restore ~10s slower than the ~130ms of real work.

To work around this, improve hybridVSockDialer so that the timeout for
connection attempts starts small and grows to the 10s maximum. This
means early CONN drops are recovered from quickly.
The backoff loop in hybridVSockDialer derives a per-attempt context via
context.WithTimeout(ctx, timeout). Some callers reach the dialer with a nil
context - notably the VM factory path, whose kataAgent connects with a nil
k.ctx. The previous dialer ignored the context, so a nil parent was harmless;
the backoff version dereferences it and panics with cannot create context
Copilot AI review requested due to automatic review settings July 27, 2026 18:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a snapshot/restore workflow for Kata “workload pods”, spanning: (1) hypervisor-level VM snapshot/restore primitives, (2) runtime sandbox/container bookkeeping to adopt a restored guest, (3) network “fencing” to safely re-bind host networking to a restored guest NIC, and (4) shim + kata-runtime CLI plumbing to trigger snapshotting and to dispatch restore based on an OCI annotation.

Changes:

  • Add VM snapshot/restore APIs across hypervisors (notably Cloud Hypervisor + QEMU) and plumb them through virtcontainers VM/Sandbox layers.
  • Implement sandbox restore flow (RestoreSandbox, RestoreContainer, FinalizeRestoreNetwork) including guest identity remapping and TC-based network fencing.
  • Add shim management /snapshot endpoint and a kata-runtime snapshot CLI, plus an annotation (io.katacontainers.restore-from) to trigger restore.

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/runtime/virtcontainers/vm.go Adds NewVMFromSnapshot, RestoreNetEndpoints, and snapshots via Save(snapshotDir) / restore via hypervisor.
src/runtime/virtcontainers/vm_test.go Updates VM tests for new Save(dir) signature and basic NewVMFromSnapshot coverage.
src/runtime/virtcontainers/virtframework.go Updates virtframework hypervisor stubs for new Save/Restore signatures.
src/runtime/virtcontainers/veth_endpoint.go Adds restore-network “fence” attach path to prepare TAP FDs without bridging/hotplug.
src/runtime/virtcontainers/stratovirt.go Adds Save/Restore methods (currently stubbed).
src/runtime/virtcontainers/sandbox.go Tracks restoreNetFence; adds Sandbox PauseVM/SaveVM/ResumeVM helpers; restores fence cleanup in removeNetwork.
src/runtime/virtcontainers/restore.go New restore implementation: rekeys persist state, restores VM from snapshot, adopts containers, and finalizes network identity.
src/runtime/virtcontainers/remote.go Updates remote hypervisor interface implementation for Save/Restore.
src/runtime/virtcontainers/qemu.go Reworks template-related logic into FileBackedMemory; implements QEMU Save/Restore via migration device state file in snapshot dir.
src/runtime/virtcontainers/qemu_test.go Updates QEMU tests for FileBackedMemory and SaveVM(dir).
src/runtime/virtcontainers/qemu_amd64.go Removes vmFactory/template flag plumbing.
src/runtime/virtcontainers/qemu_amd64_test.go Removes vmFactory-related CPU model test cases.
src/runtime/virtcontainers/pkg/vcmock/types.go Extends vcmock sandbox struct with restore hooks.
src/runtime/virtcontainers/pkg/vcmock/sandbox.go Implements vcmock methods for restore and VM pause/save/resume.
src/runtime/virtcontainers/pkg/annotations/annotations.go Adds RestoreFrom annotation key.
src/runtime/virtcontainers/pkg/agent/protocols/client/client.go Refactors HybridVsock dialer to use ctx-aware retry windows and bounded handshake reads.
src/runtime/virtcontainers/persist/api/config.go Removes template-specific persisted fields (MemoryPath/DevicesStatePath/BootToBeTemplate/BootFromTemplate).
src/runtime/virtcontainers/persist.go Drops template-specific fields from persisted config load/dump paths.
src/runtime/virtcontainers/network_linux.go Adds restore fencing helpers: prepare/activate/cleanup TC redirects; validates TC filter model on restore.
src/runtime/virtcontainers/mock_hypervisor.go Updates mock hypervisor for new Save/Restore interface.
src/runtime/virtcontainers/mock_hypervisor_test.go Updates mock hypervisor tests for SaveVM(dir).
src/runtime/virtcontainers/kata_agent.go Switches agent RPC container/exec IDs to restored guest IDs via c.agentID() / c.guestExecID().
src/runtime/virtcontainers/interfaces.go Extends VCSandbox interface with restore + VM pause/save/resume methods.
src/runtime/virtcontainers/hypervisor.go Introduces FileBackedMemoryConfig; changes hypervisor interface to SaveVM(dir) and adds RestoreVM.
src/runtime/virtcontainers/hypervisor_config_linux.go Removes template-config validation hook from config validation.
src/runtime/virtcontainers/hypervisor_config_linux_test.go Removes template-validation unit tests.
src/runtime/virtcontainers/fc.go Adds Save/Restore methods (currently stubbed).
src/runtime/virtcontainers/fc_test.go Updates firecracker tests for SaveVM(dir).
src/runtime/virtcontainers/factory/template/template_linux.go Switches factory template to FileBackedMemory, snapshots to directory, uses NewVMFromSnapshot, and patches CLH snapshot sharing mode.
src/runtime/virtcontainers/factory/factory_linux.go Resets FileBackedMemory in factory config reset.
src/runtime/virtcontainers/container.go Adds agentID() / guestExecID() helpers for restored guest ID mapping.
src/runtime/virtcontainers/clh.go Adds CLH snapshot timeout sizing; implements RestoreVM; adds PatchCLHSnapshotMemoryPrivate; implements net_fds restore staging and SCM_RIGHTS restore request.
src/runtime/virtcontainers/clh_test.go Updates CLH tests for SaveVM(dir) and changed snapshot destination URL behavior.
src/runtime/pkg/containerd-shim-v2/start.go Adds restored-sandbox start behavior: defer pause task IO arming; finalize restore network and resume at workload start.
src/runtime/pkg/containerd-shim-v2/shim_management.go Adds /snapshot handler; implements pause/save/snapshot/resume flow and snapshot manifest/config patching.
src/runtime/pkg/containerd-shim-v2/service.go Tracks restoredSandbox state.
src/runtime/pkg/containerd-shim-v2/create.go Dispatches restore via annotations.RestoreFrom; adopts workload container via RestoreContainer.
src/runtime/pkg/containerd-shim-v2/container.go Adds restorePauseIOArmPending flag to container state.
src/runtime/cmd/kata-runtime/snapshot.go Adds kata-runtime snapshot create/delete commands.
src/runtime/cmd/kata-runtime/main.go Registers the snapshot CLI command.
src/agent/src/netlink.rs Adds restore-replace interface operation keyed off a raw_flags bit; improves route EEXIST handling.
Comments suppressed due to low confidence (1)

src/runtime/virtcontainers/fc.go:958

  • RestoreVM returns nil even though Firecracker restore is not implemented. Returning success here will leave the VM un-restored and likely cause confusing follow-on failures. Return an explicit not-supported error.
func (fc *firecracker) RestoreVM(ctx context.Context, snapshotDir string) error {
	// Firecracker does not support snapshot/restore in this implementation.
	return nil
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +639 to 643
memPath := clh.config.FileBackedMemory.Path
// Double-check that the memory file exists before using it in the VM config, to avoid hitting a less clear error from cloud hypervisor when it tries to access the non-existing memory file.
if _, err := os.Stat(memPath); os.IsNotExist(err) {
return fmt.Errorf("memory file %s does not exist", memPath)
}
Comment on lines +1806 to 1809

if err := os.WriteFile(configPath, modifiedConfig, 0644); err != nil {
return fmt.Errorf("failed to write modified snapshot config: %w", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the standard should be 0600 permission bits for these artifacts.

Comment on lines +399 to +404
if err := s.doSnapshot(context.Background(), destDir); err != nil {
logger.WithError(err).Error("snapshot failed")
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor cleanup, not critical.

Comment on lines +394 to +397
destDir := strings.TrimSpace(string(body))
if destDir == "" {
destDir = filepath.Join(SnapshotBaseDir, s.id)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the very least we should be doing as the bot suggests here and normalizing the path. There are all sorts of ways such a mechanism can be used maliciously, such as with /normal/looking/path/../../../root/super-secret/not-your-snapshot/

Comment on lines +207 to +212
// snapshot storage is caller-owned, so Kata trusts the annotation path.
snapDir := filepath.Clean(restoreFrom)
if !filepath.IsAbs(snapDir) {
snapDir = filepath.Join(SnapshotBaseDir, snapDir)
}
shimLog.WithFields(logrus.Fields{"restore-from": restoreFrom, "snapshot-dir": snapDir, "sandbox": r.ID}).Info("dispatching restore-from-snapshot")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, in an attempt to make the API permissible/flexible for consumers of this API we have introduced a big security problem. Maybe we'll have to go back to named snapshots under a well-known /run/vc/vm/snapshots/ dir. This centralization would have the added benefit of allowing us to set up a tmpfs mount on that directory, meaning snapshots are cached in RAM.

Comment on lines +950 to +953
func (fc *firecracker) SaveVM(snapshotDir string) error {
// Firecracker does not support snapshot/restore in this implementation.
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, fair point here.

Comment on lines +1114 to 1122
func (s *stratovirt) SaveVM(snapshotDir string) error {
// StratoVirt does not support snapshot/restore in this implementation.
return nil
}

func (s *stratovirt) RestoreVM(ctx context.Context, snapshotDir string) error {
// StratoVirt does not support snapshot/restore in this implementation.
return nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

Comment on lines 29 to 36
} else if conf.ImagePath == "" && conf.InitrdPath == "" {
return fmt.Errorf("Missing image and initrd path")
} else if conf.ImagePath != "" && conf.InitrdPath != "" {
return fmt.Errorf("Image and initrd path cannot be both set")
}

if err := conf.CheckTemplateConfig(); err != nil {
return err
}

if conf.NumVCPUsF == 0 {
conf.NumVCPUsF = defaultVCPUs
@microsoft-github-policy-service

Copy link
Copy Markdown

@michaelxu2288 please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"
Contributor License Agreement

Contribution License Agreement

This Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
and conveys certain license rights to Microsoft Corporation and its affiliates (“Microsoft”) for Your
contributions to Microsoft open source projects. This Agreement is effective as of the latest signature
date below.

  1. Definitions.
    “Code” means the computer software code, whether in human-readable or machine-executable form,
    that is delivered by You to Microsoft under this Agreement.
    “Project” means any of the projects owned or managed by Microsoft and offered under a license
    approved by the Open Source Initiative (www.opensource.org).
    “Submit” is the act of uploading, submitting, transmitting, or distributing code or other content to any
    Project, including but not limited to communication on electronic mailing lists, source code control
    systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of
    discussing and improving that Project, but excluding communication that is conspicuously marked or
    otherwise designated in writing by You as “Not a Submission.”
    “Submission” means the Code and any other copyrightable material Submitted by You, including any
    associated comments and documentation.
  2. Your Submission. You must agree to the terms of this Agreement before making a Submission to any
    Project. This Agreement covers any and all Submissions that You, now or in the future (except as
    described in Section 4 below), Submit to any Project.
  3. Originality of Work. You represent that each of Your Submissions is entirely Your original work.
    Should You wish to Submit materials that are not Your original work, You may Submit them separately
    to the Project if You (a) retain all copyright and license information that was in the materials as You
    received them, (b) in the description accompanying Your Submission, include the phrase “Submission
    containing materials of a third party:” followed by the names of the third party and any licenses or other
    restrictions of which You are aware, and (c) follow any other instructions in the Project’s written
    guidelines concerning Submissions.
  4. Your Employer. References to “employer” in this Agreement include Your employer or anyone else
    for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent. If Your
    Submission is made in the course of Your work for an employer or Your employer has intellectual
    property rights in Your Submission by contract or applicable law, You must secure permission from Your
    employer to make the Submission before signing this Agreement. In that case, the term “You” in this
    Agreement will refer to You and the employer collectively. If You change employers in the future and
    desire to Submit additional Submissions for the new employer, then You agree to sign a new Agreement
    and secure permission from the new employer before Submitting those Submissions.
  5. Licenses.
  • Copyright License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license in the
    Submission to reproduce, prepare derivative works of, publicly display, publicly perform, and distribute
    the Submission and such derivative works, and to sublicense any or all of the foregoing rights to third
    parties.
  • Patent License. You grant Microsoft, and those who receive the Submission directly or
    indirectly from Microsoft, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable license under
    Your patent claims that are necessarily infringed by the Submission or the combination of the
    Submission with the Project to which it was Submitted to make, have made, use, offer to sell, sell and
    import or otherwise dispose of the Submission alone or with the Project.
  • Other Rights Reserved. Each party reserves all rights not expressly granted in this Agreement.
    No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are
    granted by implication, exhaustion, estoppel or otherwise.
  1. Representations and Warranties. You represent that You are legally entitled to grant the above
    licenses. You represent that each of Your Submissions is entirely Your original work (except as You may
    have disclosed under Section 3). You represent that You have secured permission from Your employer to
    make the Submission in cases where Your Submission is made in the course of Your work for Your
    employer or Your employer has intellectual property rights in Your Submission by contract or applicable
    law. If You are signing this Agreement on behalf of Your employer, You represent and warrant that You
    have the necessary authority to bind the listed employer to the obligations contained in this Agreement.
    You are not expected to provide support for Your Submission, unless You choose to do so. UNLESS
    REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND EXCEPT FOR THE WARRANTIES
    EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED UNDER THIS AGREEMENT IS
    PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF
    NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
  2. Notice to Microsoft. You agree to notify Microsoft in writing of any facts or circumstances of which
    You later become aware that would make Your representations in this Agreement inaccurate in any
    respect.
  3. Information about Submissions. You agree that contributions to Projects and information about
    contributions may be maintained indefinitely and disclosed publicly, including Your name and other
    information that You submit with Your Submission.
  4. Governing Law/Jurisdiction. This Agreement is governed by the laws of the State of Washington, and
    the parties consent to exclusive jurisdiction and venue in the federal courts sitting in King County,
    Washington, unless no federal subject matter jurisdiction exists, in which case the parties consent to
    exclusive jurisdiction and venue in the Superior Court of King County, Washington. The parties waive all
    defenses of lack of personal jurisdiction and forum non-conveniens.
  5. Entire Agreement/Assignment. This Agreement is the entire agreement between the parties, and
    supersedes any and all prior agreements, understandings or communications, written or oral, between
    the parties relating to the subject matter hereof. This Agreement may be assigned by Microsoft.

// names the source memory path (e.g. /run/vc/vm/template/memory) outside destDir.
// On restore CLH opens that path to back the memory zone, so a relocated snapshot
// (or a GC'd source) would fail. Pointing it at the in-dir memory-ranges makes the
// snapshot dir's memory self-contained. (Disks are not rewritten here; that is

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment reads as overly AI-driven. It compulsively talks about future concerns such as Disks when we just want to concisely describe the actual codepath as-written.

},
}

var deleteSnapshotCommand = cli.Command{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably the most important cleanup item. This is essentially a built-in foot-nuke for kata that allows you to rm -rf any directory on the system.

An example of foot-nuking:
kata-runtime snapshot delete --path /

would recursively delete the entire OS with no survivors.

A happy middle-ground for the PoC can be to only look in --path for the specific named artifacts [memory-ranges, persist.json, state.json, etc]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants