I built SmolBox, an open source Elixir library for running Python, JavaScript and other tools in disposable Linux VMs through SmolVM.
SmolVM provides the VM isolation. SmolBox manages the execution lifecycle under your application’s supervision tree: submitting commands, collecting results and files, handling cancellation, and tracking cleanup.
It provides:
Stable execution identity and work that can outlive the submitting process.
Separate tracking of command outcomes, result collection and cleanup.
Restart recovery through a configured durable store.
Explicit handling of uncertain outcomes when a connection drops after a command may have been accepted, without silently replaying that work.
Python is the example in the introductory article, but prepared guest images can include other runtimes and tools.
You operate the SmolVM workers and prepare the images. Host resource limits, networking and worker access controls depend on your deployment. The documentation describes the specific Linux configuration we have tested and its remaining boundaries.
How are you handling these workloads in your Elixir applications today? I’m interested in what already works well, what remains difficult, and whether SmolBox fills a useful gap.
I wrote a follow-up about testing SmolBox in a disposable Linux lab with nested KVM.
The main question was: after the workload or its worker fails, can we account for the execution, remove its resources, and run the next one?
The article covers three situations:
Full storage: deleting a VM failed because the worker needed space to update its database. Separating control metadata from workload storage allowed cleanup to finish in the later experiment.
Memory exhaustion and worker failure: SmolBox recovered the execution record while preserving an unknown command outcome. In the tested cases, recovery did not send the command again.
An unresponsive outer VM: recovery running on the physical host terminated the frozen environment and prepared a replacement from a clean baseline.
One useful distinction for Elixir applications: restarting a process, knowing what its external command did, and verifying resource cleanup are separate problems.
The post includes the setup, evidence, recovery procedures and limitations. These results apply to the documented Linux configuration; they do not establish portable resource guarantees for every deployment.
A later development: the experiments used smolvm 1.14.1. The shared storage deletion issue described in the article was subsequently fixed in 1.14.6 and retested through SmolBox.
For those managing external workloads from Elixir, how do you test recovery when the worker disappears after accepting a command?
An update on SmolBox: version 0.2.0 adds managed persistent machines alongside disposable execution.
You can now keep a machine across multiple commands, restart the Elixir application managing it, and reconnect to the same machine with its files still there. Deletion is explicit.
I built a small Phoenix LiveView workspace to show how this works. It includes a browser terminal, command history, file uploads and downloads, and a web service running inside the guest.
The new walkthrough takes you through creating a workspace, editing a page, reading those files from another command, restarting Phoenix, and then stopping and starting the machine.
One distinction matters throughout: the machine and its files persist; each command and terminal connection still has its own lifecycle. Restarting the controller doesn’t restore a terminal session or turn an unknown command outcome into a known one. PostgreSQL stores the management records, while guest files remain on the worker’s disk.
The article includes runnable Elixir examples, setup instructions, a short demo, and a comparison of persistent machines, disposable executions, and checkpoints.
An update on SmolBox: 0.3.0 adds exports, checkpoints and branches for reusing prepared VM state.
Following the persistent workspace example above, I wrote a guide around a practical question: once you have an environment ready, how much preparation should the next machine repeat?
The three options preserve different things:
Export: publish supported disk contents from a stopped machine. The next machine boots fresh.
Checkpoint: save disk and RAM, then explicitly restore them into a separate machine.
Branch: create a child with disk and RAM state from a running source on the same worker, while retaining a dependency on that source.
The article compares these with fresh provisioning and simply running another command on the existing machine. It includes physical Linux measurements, an interactive explanation, and a runnable Phoenix LiveView example.
One interesting result: saving the prepared data to disk already avoided most of the repeated work in our workload. Preserving RAM helped further, but the checkpoint and branch timings overlapped. The state you need to preserve and the cleanup you can manage matter more than picking the smallest number.
Checkpoint and branch support currently targets approved idle, offline bare guests. This is not arbitrary application cloning, and the post explains those limits alongside retention and cleanup.
For your workloads, would keeping the prepared files be enough, or is there something in memory that would be expensive to rebuild?
Following the discussion above about reusing prepared VM state, I wrote a walkthrough about a related question: what if you want to replace the machine but keep the project files?
SmolBox 0.4.0 introduced managed local volumes. They have their own identity and lifetime, so deleting a machine releases its attachment without deleting the volume. A replacement on the same worker can then mount the existing data.
The article uses SmolBox 0.4.1 and follows one file through the complete lifecycle:
• Write it from the original machine.
• Delete that machine and exit the Elixir controller.
• Start a new controller with the same PostgreSQL store.
• Read and modify the file from a replacement machine.
• Verify that a read-only attachment rejects writes.
• Explicitly delete the machines and volume.
PostgreSQL retains the management records; the worker retains the actual files. After the original machine is gone, the volume and its storage reservation remain.
The walkthrough includes an interactive diagram, a runnable Elixir example and verification on physical Linux. It also covers the important boundaries: one exclusive attachment per volume, no cross-worker migration, and no automatic backup.
For the external workloads you run from Elixir, what would be most useful to keep across machine replacements: project files, dependency caches, or generated artifacts?