PodMotion is alpha software (v0.1.0-alpha). APIs and behaviors may change without notice. Not recommended for production workloads.

How Migration Works

PodMotion migrates a running Kubernetes pod from one node to another without stopping it. It is the container-layer equivalent of hypervisor live VM migration: the live process tree is checkpointed with CRIU, its memory and open state are transferred to the destination node, and the pod is restored there — while the workload keeps serving.

This page is the conceptual on-ramp. It explains the shape of a migration, the two things that make live migration hard (in-flight TCP connections and attached storage), and — precisely — what has and has not been proven so far. For field-level detail, follow the links to the CRD Reference, Observability Reference, and Security Architecture.

WARNING

PodMotion is alpha software (v0.1.0-alpha) and is not production-ready. The claims on this page are scoped deliberately narrowly: read the What is proven today and What is not yet proven sections for the exact boundary.

The interface: one custom resource

The only supported interface in v0.1.0-alpha is the PodMigration custom resource, applied with kubectl apply. There is no active kubectl podmotion plugin — a plugin exists only as inactive scaffolding in a separate repository and is not part of the supported v0.1.0-alpha interface. You describe the pod you want to move; the controller drives the rest.

apiVersion: migration.podmotion.io/v1alpha1
kind: PodMigration
metadata:
  name: migrate-my-app
  namespace: default
spec:
  podName: my-app-pod          # required
  podNamespace: default        # required
  targetNodeName: worker-a     # optional; scheduler picks if empty
  # tcpPreservationMode defaults to None. Opt in with Strict for TCP continuity.

See the CRD Reference for every field, and Getting Started to install and run your first migration.

The shape of a migration

A migration moves through a phase sequence. The connection-preserving path (Strict or BestEffort) is, simplified:

Pending → SocketInventory → Validating → DestinationPrewarm → PreCopyMemory
        → ZeroWindowArm → FinalFreezeStateCapture → OverlayHandoff
        → RestoreSocketReattach → DisengageHold → TCPVerifying → ServiceVerifying
        → CutoverComplete → Complete

Every mode — None, BestEffort, and Strict — traverses all twelve pipeline phases as status.phase transitions (the sequence above shows 14 states total: the 12 pipeline phases bracketed by the starting Pending and terminal Complete states), so this is the sequence you will see when you watch a migration with kubectl get podmigration -w. What changes with the mode is the work done inside those phases. In the default None mode (no TCP continuity), the connection-preservation work inside several phases — SocketInventory, DestinationPrewarm, ZeroWindowArm, OverlayHandoff, DisengageHold, and TCPVerifying — is a no-op that advances immediately: the phase is still entered and visible, but it does no TCP-specific work.

The load-bearing steps:

  1. Pre-copy. For PreCopy mode (the default), CRIU iteratively copies the pod's dirty memory pages to the destination while the pod keeps running, so the amount left to move at freeze time is small.
  2. Freeze and checkpoint. The pod is briefly frozen and CRIU captures the final process state — memory, open file descriptors, and, for a connection-preserving migration, the state of established TCP sockets.
  3. Transfer. The checkpoint image is moved to the destination node.
  4. Restore. CRIU rebuilds the process tree on the destination node. This is the hardest step, and the one where environment-specific gaps show up first (see What is not yet proven).
  5. Verify, then cut over. The restored pod is probed; only after it is confirmed serving is traffic handed over.

The full phase vocabulary — 22 phase constants — together with the complete set of status condition types lives in the CRD Reference and Observability Reference. Status conditions on the PodMigration CR are the primary way to observe progress: the controller-runtime Prometheus /metrics endpoint is disabled by default (--metrics-bind-address defaults to "0"), so in a default install the CR's status conditions are the observability surface you read.

The source pod is never deleted early

Throughout a migration, the original pod stays alive. The controller only permits the source pod to be deleted once a status condition confirms the destination pod is verified serving traffic and healthy — the specific condition and its owning ADR depend on mode (TrafficVerified / ADR-0012 outside Strict mode; ServiceContinuityVerified / ADR-0352 under Strict). See the CRD Reference for the exact condition set per mode. If anything fails before that point, the migration rolls back to the still-running source pod.

TCP connection continuity is opt-in

Moving a stateless pod is one thing. Moving a pod that is holding an established TCP connection — and keeping that connection alive across the move, with no reset visible to the client — is the hard case.

TCP continuity is off by default. spec.tcpPreservationMode defaults to None, which skips TCP verification entirely. You opt in explicitly:

ModeBehavior
None (default)No TCP continuity; stateless workloads.
StrictMigration hard-fails and rolls back if TCP continuity cannot be verified.
BestEffortAttempts preservation; warns rather than rolling back on failure.

When continuity is verified, the measure is the TCP sequence delta. A sequence delta of zero (seq_delta=0) means the TCP sequence numbers on both ends of the connection matched exactly — neither side saw a single byte of drift, and the connection was never dropped.

What is proven today

Everything below is proven on one substrate only: a standing 3-node KVM/libvirt kubeadm cluster, on amd64, running:

  • Ubuntu 24.04
  • Kubernetes v1.35.x
  • Cilium CNI v1.17.6 — tunnel (geneve) encapsulation, with per-endpoint /32 routes (endpointRoutes) for preserved-IP cross-node reachability
  • CRIU 4.2 on every node

This KVM cluster is the current proven substrate. (An older Flannel VXLAN path is retained only as a rollback baseline; it is not the proven substrate, and earlier arm64 / Flannel figures do not describe this platform.)

On that cluster, in a single test run, two individual single-workload migrations were demonstrated end-to-end, each moving from one worker node to another and reaching phase=Complete:

  • Redis, in Strict mode, holding a live TCP connection under load. The held client connection continued across the cutover with no break, verified byte-exact (1 of 1 connections) with a send/receive sequence delta of 0/0 — zero sequence drift, no dropped connection. A canary value written before the move was read back correctly through the service afterward. Elapsed 33s; freeze window 602ms.
  • Postgres, under continuous write load, backed by shared (ReadWriteMany) storage. The storage volume re-attached on the destination node and writes continued with zero write loss: the row counter advanced normally (8 → 16) through the migration window with 0 post-migration write errors. Elapsed 43s; freeze window 766ms. This exercises ReadWriteMany re-attach only — it does not exercise or imply live block-storage migration, which remains deferred to v1.2.

Both workloads completed their migrations repeatedly and cleanly within that same run.

NOTE

This is deliberately narrow. These are two individual, single-workload migrations on the KVM cluster. PodMotion does not claim that live migration reaches Complete reliably across arbitrary workloads, that a full connected multi-tier application moves as a single unit, or that any substrate other than this KVM cluster is proven. The scope is exactly the two cases above.

What is not yet proven

The KVM cluster is the only proven substrate. Other substrates — including cloud-hosted kubeadm clusters — are not yet proven viable; the KVM result does not carry over automatically. Restore is where substrate differences tend to show up first (kernel, CNI, and storage-driver behavior on the destination node all factor into it), which is part of why proving a new substrate takes dedicated work rather than being assumed from the KVM result. Proving additional substrates is ongoing, and this page will be updated as that work is verified.

See also