Skip to content
View original post on X: Eldar Hasanov
Published
34/100

Portal moves running Python workloads between machines, preserving progress

AIPortal, a new infrastructure product, moves running Python workloads between machines while preserving their progress and enabling use of cheaper compute. The underlying principle was recently accepted to the ACM/IFIP Middleware conference.
Eldar HasanovVerified on X
@eldar_hsnv
View on X

Many people were asking about previous approaches to portable workloads after we launched Portal.

Here is a brief overview of the history behind this direction.

You know, the shoulders-of-giants stuff. https://x.com/i/article/2117055859675283456

A brief history of portable computing

We just launched Portal, infrastructure for moving running Python workloads between machines, preserving their progress and making cheaper compute usable. It is based on the underlying principle we developed, which was recently accepted to the ACM/IFIP Middleware conference.

But moving computation is quite an old idea, and the history helps explain why we built it at the application layer.

The question I care about is simple: to what extent is usable program state decouplable from the runtime? In other words, if the machine needs to change halfway through a job, how much of the work can come with it?

Different systems have previously answered that at different layers. Each answer makes something easier and leaves something else to the application, dancing around trade-offs (as every system problem entails).

From portable code to portable execution

An early influential approach in the 1970s came from C and UNIX. Johnson and Ritchie's 1978 paper [1] described moving programs between machines while isolating the parts that depended on the hardware. You could reuse the source and compile it for another architecture. Your running stack, open connections, and accumulated memory did not come with it, but compilation was the first move towards portability. Eventually, they won the Turing Award for this work.

By the late 1980s, researchers were looking into moving work that had already started. A system called Condor [2] used otherwise idle workstations, with checkpointing helping jobs survive when their owners needed the machines back. Another system called Sprite [3] implemented process migration inside a custom distributed operating system. These were practical attempts to use available compute without throwing away progress.

The price was, of course, integration and compatibility. Sprite migrated between machines of the same architecture and relied on its distributed OS. Condor's later Standard Universe [4] required relinking applications and restricted threads, subprocesses, and other resources. Transparent migration always had a boundary around what it could preserve, and due to all this friction, neither really made it out of the research world fully.

A parallel line of work moved that boundary into the language runtime. Emerald [5], described in 1987–88, could move objects with active method invocations. The runtime understood enough about execution to relocate it. That gave finer control over where computation happened, at the cost of cooperation between the language, compiler, and runtime.

The mobile-agent era

In the 1990s, this became the mobile-agent idea. General Magic's Telescript [6] gave programs a go operation: move to another host, then continue. Dartmouth's Agent Tcl [7] captured interpreter state and resumed after agent_jump. Cloning and sending agents to multiple destinations were already part of this research.

The attraction was obvious. You can send computation to the data, keep working across unreliable connections, and make movement a normal operation in the program. But every destination needed the right runtime. Authentication, resource limits, debugging, and external side effects still needed answers. Native extensions needed their own state-handling support.

Java [8] offered a different kind of portability than traditional compiled languages like C. Compiled bytecode that compatible JVMs could execute on different platforms. The whole pitch of the language was "compile once and run anywhere", something you just could not do before.

IBM Aglets built mobile agents on that ecosystem, serializing Java objects and restarting through lifecycle callbacks. It did not carry the JVM thread's execution stack. That distinction is often called weak versus strong mobility: moving code and data versus also carrying the supported continuation. [9]

So yes, portable execution state has decades of prior art. The interesting question is which workloads a system supports, and how much work adopting it requires.

Moving VMs and processes

In the 2000s, VM live migration took a broader approach: carry the operating system too. VMware [10] shipped VMotion in 2003; Xen's [11] 2005 paper described moving running VMs with short interruptions by copying memory while execution continued, then transferring the remaining state. Applications could remain unmodified.

That is a powerful tradeoff when you control the infrastructure. The guest keeps its OS and process environment, but the destination must present compatible virtual hardware. Storage, networking, and devices also need coordination. Capturing more of the machine generally means more state and more infrastructure to manage, although incremental copying and memory sharing can reduce the cost.

Process checkpointing developed alongside it, as a very compelling smaller-scale alternative. DMTCP's [12] 2009 design captured unmodified Linux computations in user space, including distributed processes and threads. Another fundamental approach, CRIU [13], started in 2011, built on Linux interfaces to reconstruct processes and their resources. You could preserve substantially more than an application author's hand-written checkpoint without carrying an entire guest OS.

The restriction is compatibility, rather than an identical cloud instance name. Native registers, stacks, and machine code retain assumptions about the CPU and execution environment. An x86 process image cannot simply resume natively on Arm; even two x86 machines can differ in required CPU features. Compatible machines in different clouds can work. Unsupported devices and external resources still need handling. [14], [15]

Containers, portable runtimes and replay

The 2010s made packaging much easier. Docker popularized shipping applications with their userspace dependencies. But a container image contains files and configuration, not the memory of a running process. Docker's separate, experimental checkpoint feature uses CRIU, so it inherits that kind of compatibility boundary. A reproducible environment helps you restart; continuation needs an additional mechanism. [16], [17]

WebAssembly [18], described in its 2017 PLDI paper, offers another portable execution target. It reduces dependence on a particular CPU instruction set. Host imports still define access to files, networks, and other services, and moving a running instance requires additional support. Portable instructions alone do not make every resource portable. [19]

Other approaches deliberately reconstruct progress at a higher level. Application checkpoints have a long history; today's PyTorch [20] training checkpoint is a familiar example. Save weights, optimizer state, and whatever else the run needs, then recreate the program around them. This can cross hardware boundaries efficiently. The application has to know what to save, including less obvious state such as sampling position or preprocessing artifacts.

Durable workflow systems such as Temporal [21] reconstruct logical execution from recorded history. Completed activity results can be reused during replay. That works well for orchestration, but it does not automatically preserve a training process's tensors or a simulator's internal state. Retried activities also need appropriate handling of external side effects.

GPUs, snapshots and cloud boundaries

Meanwhile, native GPU checkpointing has become much more capable. NVIDIA's 2024 CUDA [22] checkpointing support lets submitted GPU work finish, copies device memory to host memory, and releases GPU resources so the CPU process can be checkpointed. CRIUgpu [23] extended the integration to GPU workloads and containers. This preserves execution machinery that an application-level approach deliberately reconstructs.

The current NVIDIA restore contract requires the same GPU chip type and enough memory. A native T4 checkpoint therefore cannot simply be restored onto an H100. Loading model tensors onto an H100 through PyTorch is a different operation: the new runtime rebuilds the device-specific machinery. Both approaches are useful; they preserve different things. [24]

Modern sandbox platforms (there is a myriad of them now) make snapshots accessible through APIs. E2B [25], Modal [26] and Daytona [27] expose ways to preserve or reuse environments, with different rules for memory, files, GPUs and restoration. These are useful building blocks, but a service's restore API does not establish a common execution format another provider can accept. Modal's sandbox memory snapshots, currently in alpha, require the original instance type and exclude GPUs.

This is not an inherent ban on exporting VM state. Firecracker writes snapshot files. The harder question is whether the destination can reconstruct their CPU, device, and environment assumptions. Nested virtualization can give you control of an inner hypervisor on supported cloud instances, but it does not remove those assumptions. AWS, Google Cloud, and Azure support it on selected offerings. Google warns that nested CPU-bound workloads may lose 10% or more in performance; the overhead depends on the workload. [28], [29], [30], [31]

Why we chose the application layer

That history is why we built Portal's mechanism at the Python application layer. We capture session state into a portable capsule and unpack it at the destination environment. The underlying runtime can change from a microVM to a container or host process, across any OS, CPU, and GPU combinations.

A correct move has to preserve relationships as well as values. A tensor view must still share storage with its base. An optimizer must still reference the model's actual parameters. Requests admitted before capture must finish before the snapshot is taken. If restoration fails before commit, the source must remain authoritative.

Those are the problems our approach addresses.

What I want from this is straightforward: choose hardware for the work the program is doing now, while keeping the state it has already built. Eventually, decouple the runtime from hardware entirely.

That is the direction we're building with Portal.

You can try it today at: https://portal.clusy.io/.

References

[1] Johnson, S. C., and Ritchie, D. M. “Portability of C Programs and the UNIX System.” 1978.

[2] Litzkow, M. et al. “Condor — A Hunter of Idle Workstations.” 1988.

[3] Douglis, F., and Ousterhout, J. “Transparent Process Migration: Design Alternatives and the Sprite Implementation.” 1991.

[4] HTCondor. “Current Limitations.” Historical version 8.8 documentation.

[5] Jul, E. et al. “Fine-Grained Mobility in the Emerald System.” 1988.

[6] Dartmouth. “Workshop on Mobile Agents: Presentation Summaries.” 1996.

[7] Gray, R. S. “Agent Tcl: A Flexible and Secure Mobile-Agent System.” 1996.

[8] Sun Microsystems. Java language white paper. 1995.

[9] IBM. Aglets Specification 1.1. 1998.

[10] Nelson, M. et al. “Fast Transparent Migration for Virtual Machines.” 2005.

[11] Clark, C. et al. “Live Migration of Virtual Machines.” 2005.

[12] Ansel, J. et al. “DMTCP: Transparent Checkpointing for Cluster Computations and the Desktop.” 2009.

[13] CRIU Project. “History.” Project documentation.

[14] CRIU Project. “CPU capabilities.” Compatibility documentation.

[15] CRIU Project. “What cannot be checkpointed.” Documentation.

[16] Docker. “What is a container?” Documentation.

[17] Docker. “docker checkpoint.” Documentation.

[18] Haas, A. et al. “Bringing the Web up to Speed with WebAssembly.” PLDI 2017.

[19] WebAssembly. “Portability.” Official documentation.

[20] PyTorch. “Saving and Loading Models.” Documentation.

[21] Temporal. “Workflows.” Documentation.

[22] NVIDIA. “Checkpointing CUDA Applications with CRIU.” 2024.

[23] Stoyanov, R. et al. “CRIUgpu: Transparent Checkpointing of GPU-Accelerated Workloads.” 2025.

[24] NVIDIA. “CUDA Checkpointing.” CUDA Driver API documentation.

[25] E2B. “Snapshots.” Documentation.

[26] Modal. “Sandbox Snapshots.” Documentation.

[27] Daytona. “Images.” Documentation.

[28] Firecracker. Snapshot compatibility and versioning documentation.

[29] Amazon Web Services. “Nested virtualization.” EC2 documentation.

[30] Google Cloud. “Nested virtualization overview.” Compute Engine documentation.

[31] Microsoft Azure. “Ddsv5 size series.” VM capability documentation.

[32] Hasanov, E., Lin, J., and Ali, M. F. A. S. “SOMA: Transactional Migration of Live Python Sessions across Heterogeneous Runtimes.” 2026. Accepted to ACM/IFIP Middleware; forthcoming.

Source details

Eldar Hasanov · x.comPublished · added here

Original title: Many people were asking about previous approaches to portable workloads after we launched Portal.