PETS
CATTLE.
Spin your agent a throwaway micro-VM. Run the task inside it. Destroy the box.
You have one machine you babysit.
- The agent runs on it. It leaves files, mutates state, installs junk.
- You clean up after every run, so cleanup is now part of the job.
- You hesitate before letting it run wide, because the blast radius is your actual work.
- That box is a pet. It has a name, a history, and a bad day ruins your week.
Tonight: stop nursing one box.
The container half-answer
A container sandbox keeps the agent off your laptop, and that is a genuine improvement. It is not the whole answer.
It still shares your kernel, and the boundary is fuzzy at the edges. Escape, resource bleed, and "works in my container" all live in that gap.
Why a whole VM, and not just a container.
Container
- Shared kernel, namespaced.
- Fast and light. This is the reason it won.
- The isolation boundary IS the host kernel's attack surface.
Full VM
- Its own kernel. Its own everything.
- A hostile or runaway agent is contained at the hardware virtualization line, not at a namespace.
- Historically the cost was boot time and weight.
That cost is what changed. microVMs plus snapshot and restore make a full VM cheap enough to throw away. The isolation you wanted is now as disposable as the container you settled for.
Three tools. Same lifecycle.
Firecracker
The thing under Lambda and Fargate. Minimal device model, one process per VM. Built for exactly this: one short lived VM per task.
Lima
Linux VMs on macOS and Linux from a YAML template, with a shared mount and cloud-init built in. Easiest way in.
Multipass
One command Ubuntu VMs. multipass launch, cloud-init native, snapshot support, a CLI you can hold in your head.
On boot times: AWS publishes roughly 125ms to userspace for Firecracker's own minimal device model on their hardware. Treat that as the floor of what the technology can do, not as a promise about your laptop. Nested virtualization on a Mac will be slower, and the number that actually matters tonight is restore-from-snapshot, which you will measure yourself on slide 10.
cloud-init bootstraps it. The snapshot makes it instant.
cloud-init is the bootstrap contract
One YAML file the VM reads on first boot: install packages, write files, drop your agent and its keys, run a command. The same file works across Firecracker, Lima and Multipass, which is why you learn it once and keep it when you change backend.
snapshot and restore is the speed
Bootstrap once, snapshot the warm box, then restore from that snapshot per task. Spin-up stops being "install everything" and becomes "thaw a box that is already ready".
Architecture, illustrative
One ephemeral VM per agent task.
spin
Restore a clone from the golden snapshot. No install, no wait.
inject
cloud-init drops the task and the agent. Secrets arrive as env, now.
run
The agent works inside the box. Full isolation while it does.
collect
Copy out only the artifact you asked for. The diff, the file. Not the home dir.
destroy
Kill the VM. Nothing leaks back, nothing accumulates.
Repeat that and the boxes stop being individuals. Every agent task is cattle. The value moved out of the machine and into two files you can rebuild from: the cloud-init and the script.
Watch one box live and die.
| phase | what you see | timed |
|---|---|---|
| spin | the box appears in multipass list | boot |
| run | the agent does a real task inside it | run |
| collect | one artifact lands on the host | — |
| destroy | multipass list goes empty | teardown |
Timings read off the live run, not from this slide
The aha
The host filesystem is untouched. Check it after: no new deps, no stray files, no half-finished edit.
Only the one artifact you asked for came back. That is not a side effect of the tooling. It is the design.
Set up your VM tool.
Pick one and stay on it
Multipass is the default tonight because it is one command to a working Ubuntu box and it has snapshots built in.
Lima is the same shape if you prefer YAML templates: limactl start --name=agentbox template://ubuntu-lts.
Do not try both in the hands-on hour. You want a baked snapshot by 21:00, not two half-installs.
brew install --cask multipass multipass version multipass find # available images, look for 24.04
sudo snap install multipass multipass version multipass find # available images, look for 24.04
# Multipass on Windows needs a hypervisor: Hyper-V (Windows Pro or # Enterprise) or VirtualBox. Windows Home has neither by default. # # If you have Hyper-V: winget install Canonical.Multipass multipass version # Otherwise use WSL2 and run the Linux tab inside it. This is the # path that works on every Windows laptop in the room: wsl --install -d Ubuntu-24.04 # then, inside the WSL shell, follow the Linux tab.
# Crostini does not expose nested virtualization on most Chromebooks, # so Multipass will not start a VM locally. Do not fight it tonight. # # Use a remote Ubuntu box and run the whole lab there over SSH: ssh you@your-box # then follow the Linux tab on that machine. # # Everything after this slide is identical: the cloud-init file, the # snapshot, and the script do not care that the host is remote.
Write the cloud-init bootstrap.
# cloud-init.yaml - runs on FIRST boot only #cloud-config package_update: true packages: - git - curl - python3-pip runcmd: # install the agent runtime into the image - [ bash, -lc, "curl -fsSL https://claude.ai/install.sh | bash" ] # NOTE: no keys here. Secrets ride in at spin time, see below.
# cloud-init.yaml - runs on FIRST boot only #cloud-config package_update: true packages: - git - curl - python3-pip runcmd: # install the agent runtime into the image - [ bash, -lc, "curl -fsSL https://claude.ai/install.sh | bash" ] # NOTE: no keys here. Secrets ride in at spin time, see below.
# Identical file. cloud-init is read by the GUEST, so the host OS is # irrelevant. Save it with LF line endings, not CRLF, or cloud-init # will fail to parse the YAML inside the VM. #cloud-config package_update: true packages: - git - curl - python3-pip runcmd: - [ bash, -lc, "curl -fsSL https://claude.ai/install.sh | bash" ]
# Identical file on your remote box. #cloud-config package_update: true packages: - git - curl - python3-pip runcmd: - [ bash, -lc, "curl -fsSL https://claude.ai/install.sh | bash" ]
The rule that matters
Secrets ride in at spin time as env. They are never baked into the snapshot.
The snapshot is code and deps only. That way a leaked or shared snapshot is worthless to whoever gets it, and you can rebuild it in public.
Tonight the agent is Claude Code pointed at z.ai, and the labs run on Nebius Token Factory credits. Both arrive as environment variables on the spin, on slide 11.
Bake a golden snapshot.
# launch ONCE with the bootstrap and let cloud-init finish multipass launch 24.04 --name golden \ --cloud-init cloud-init.yaml --memory 2G --disk 10G # block until the bake is actually done, do not eyeball it multipass exec golden -- cloud-init status --wait # a snapshot requires the instance to be stopped multipass stop golden multipass snapshot golden --name baked multipass list --snapshots # golden.baked should be listed
# launch ONCE with the bootstrap and let cloud-init finish multipass launch 24.04 --name golden \ --cloud-init cloud-init.yaml --memory 2G --disk 10G # block until the bake is actually done, do not eyeball it multipass exec golden -- cloud-init status --wait # a snapshot requires the instance to be stopped multipass stop golden multipass snapshot golden --name baked multipass list --snapshots # golden.baked should be listed
# Inside WSL2, or on Windows Pro with Hyper-V. Same commands. multipass launch 24.04 --name golden ` --cloud-init cloud-init.yaml --memory 2G --disk 10G multipass exec golden -- cloud-init status --wait multipass stop golden multipass snapshot golden --name baked multipass list --snapshots
# On your remote Ubuntu box. Same commands as Linux. multipass launch 24.04 --name golden \ --cloud-init cloud-init.yaml --memory 2G --disk 10G multipass exec golden -- cloud-init status --wait multipass stop golden multipass snapshot golden --name baked multipass list --snapshots
golden.baked is now your reusable warm box.
Every task gets a fresh clone of it. Not a fresh install. This is the step that turns a two minute spin-up into a few seconds.
cloud-init status --wait is not optional. Snapshot too early and you bake a half-installed box, then every clone inherits the same half-install.
Time the next slide's spin with and without the snapshot. That difference is the number this whole pattern is built on, and it is yours, not a vendor's.
The spin-and-destroy script.
Clone from the snapshot, inject the task, run the agent, collect the artifact, destroy the box. This file is the deliverable.
#!/usr/bin/env bash
# run-in-throwaway.sh <task-file>
set -euo pipefail
TASK="${1:?usage: run-in-throwaway.sh <task-file>}"
BOX="task-$(date +%s)"
# 1. SPIN: a fresh box cloned from the baked snapshot
multipass clone golden --name "$BOX" && multipass start "$BOX"
# 2. INJECT: the task, plus secrets as env (never in the snapshot)
multipass transfer "$TASK" "$BOX":/home/ubuntu/task.md
multipass exec "$BOX" -- bash -lc "
export ANTHROPIC_BASE_URL='$ZAI_BASE_URL'
export ANTHROPIC_AUTH_TOKEN='$ZAI_KEY'
mkdir -p ~/out && cd ~
claude -p \"\$(cat task.md)\" --dangerously-skip-permissions
"
# 3. COLLECT: ONLY the artifact. Not the home dir.
multipass transfer -r "$BOX":/home/ubuntu/out ./out-"$BOX" || true
# 4. DESTROY: the box is cattle
multipass delete "$BOX" && multipass purge
echo "box $BOX gone; host untouched"
#!/usr/bin/env bash
# run-in-throwaway.sh <task-file>
set -euo pipefail
TASK="${1:?usage: run-in-throwaway.sh <task-file>}"
BOX="task-$(date +%s)"
# 1. SPIN: a fresh box cloned from the baked snapshot
multipass clone golden --name "$BOX" && multipass start "$BOX"
# 2. INJECT: the task, plus secrets as env (never in the snapshot)
multipass transfer "$TASK" "$BOX":/home/ubuntu/task.md
multipass exec "$BOX" -- bash -lc "
export ANTHROPIC_BASE_URL='$ZAI_BASE_URL'
export ANTHROPIC_AUTH_TOKEN='$ZAI_KEY'
mkdir -p ~/out && cd ~
claude -p \"\$(cat task.md)\" --dangerously-skip-permissions
"
# 3. COLLECT: ONLY the artifact. Not the home dir.
multipass transfer -r "$BOX":/home/ubuntu/out ./out-"$BOX" || true
# 4. DESTROY: the box is cattle
multipass delete "$BOX" && multipass purge
echo "box $BOX gone; host untouched"
# Run this INSIDE WSL2 as the Linux tab. A PowerShell port works but # the quoting around the agent invocation is the part that bites, and # you do not want to debug quoting during the hands-on hour. wsl -d Ubuntu-24.04 # then: bash run-in-throwaway.sh my-task.md
# On your remote Ubuntu box, identical to the Linux tab. bash run-in-throwaway.sh my-task.md
Firecracker, one level deeper.
# one microVM = one firecracker process + an API socket
firecracker --api-sock /tmp/fc-$BOX.sock &
# configure the boot source over the socket
curl --unix-socket /tmp/fc-$BOX.sock -X PUT \
'http://localhost/boot-source' \
-d '{"kernel_image_path":"vmlinux",
"boot_args":"console=ttyS0 reboot=k panic=1 pci=off"}'
# ... drives and network interfaces configured the same way ...
curl --unix-socket /tmp/fc-$BOX.sock -X PUT \
'http://localhost/actions' \
-d '{"action_type":"InstanceStart"}'
# destroy is: kill the process.
kill %1
The point, made not required
Same lifecycle. Spin, run, destroy. The difference is that there is no CLI doing it for you: you drive a REST API over a unix socket, and the VM is one process you can kill.
That is why it is the layer under Lambda and Fargate. It is also why you would not hand-roll it for a workshop.
In production you use something that wraps the socket: firecracker-containerd, Fly.io machines, or Modal sandboxes. You are then buying the same pattern you just built by hand.
Where this bites.
Secrets
Never bake z.ai or Nebius keys into the snapshot. They ride in as env per spin, so a leaked snapshot is worthless. This is the whole reason the bake and the run are separate steps.
The collect step is the only door back
Be deliberate about what you copy out. If you copy the whole home directory, you defeated the isolation and reintroduced everything you were avoiding.
--dangerously-skip-permissions
Safe here ONLY because the box is disposable. Full autonomy inside, zero blast radius outside. That trade is the point. Never run it on a pet.
Networking
A fully sandboxed box can still phone home. Filesystem isolation is not network isolation. Cap egress if the task is untrusted, and skip shared mounts.
Snapshot drift
Rebuild the golden snapshot when deps move. A stale baked box slowly becomes its own pet, which is the failure mode this pattern exists to prevent.
Disk
multipass delete without purge leaves the box recoverable and the disk used. The script pairs them for a reason. Check multipass list --snapshots occasionally.
What you have at 22:00.
- A working
run-in-throwaway.sh: fresh VM per task, agent runs inside, artifact collected, box destroyed. - A golden snapshot, so every future spin-up is a thaw rather than an install.
- A clear line for when a full VM beats a container: full OS isolation, its own kernel, safe for a task you do not trust.
- The two files that hold all the value, both rebuildable from scratch: the cloud-init and the script.
Spin. Run. Destroy.
Nothing accumulates.
Run it tonight, and after.
Your ticket includes z.ai plus Claude Code for the session, and Nebius Token Factory credits to run the labs.
In the room
- z.ai + Claude Code drives the agent INSIDE the throwaway box, provisioned for the workshop.
- Nebius Token Factory credits are the compute for the hands-on hour, scoped per event.
The tools, and their docs
- Firecracker the microVM monitor under Lambda and Fargate.
- Multipass tonight's on-ramp, from Canonical.
- Lima the YAML-template alternative.
- cloud-init the bootstrap contract all three share.
Where this goes in production
- Fly.io machines and Modal sandboxes sell the same pattern as a service. Once you have built it by hand, you know exactly what you are buying.
You built it. Now join the series.
VCN is SF's builder night for people who code with agents. One real thing each session, working before you leave.
Come back
- Telegram: t.me/+EBFzKXmJAVk5ZGU0
- Site: vibecodingnights.com
- Every deck: immersivecommons.com/presentations
Two formats
- Nights · Wednesdays, Floor 10.
- Mornings · Saturdays, Floor 9.
- We take builder-led talks. Bring a pattern you shipped.
Hosted by Rayyan Zahid (Immersive Commons) · Michalis Vasileiadis (Hacker Bob) ·
Eric Mockler (AI Geneticist) · Devinder Sodhi (Learning Layer Labs)
Facilitator: Rayyan Zahid