dev-ore devbox: golden Lima VMs and disposable clones for safer coding
Introduction
dev-ore devbox is a small, Apache-2.0-licensed set of Lima YAML templates and provision scripts for a pattern many teams arrive at independently: bake one golden Linux VM, then spawn short-lived clones when you need a clean environment. Work happens inside the guest—compilers, package managers, and agent-driven shell sessions stay off the host’s daily driver—while you edit from the host and reach the VM over SSH using Lima’s generated config.
The templates target Lima 2.x (minimumLimaVersion: 2.0.0 in the recipes). They show how to clear cloud-init on the golden so clones do not inherit a broken first-boot story, how Lima provision with mode: system runs shell “bake” scripts you can customize like cloud-init-style setup, how mode: data copies a host file into the guest at limactl create time, and how ForwardAgent plus host ssh-agent (or IdentityAgent) lets git@… work without writing private keys onto the disposable disk.
This article is not a substitute for reading Lima’s docs—it explains why we published the project and how the moving parts fit next to the rest of this site’s isolation writing: Lima on the desktop, Lima vs Firecracker microVMs, and the AI coding devbox ladder.
Why disposable guests help
A dedicated Linux kernel in the VM closes a large class of “weird toolchain / compromised package post-install” worries that apply when everything shares one host userspace. Disposable clones add another lever:
- Rogue or overly helpful coding agents can delete the wrong tree, run surprising
curl | shinstallers, or leave credentials in shell history—deleting the VM resets that blast radius to one guest disk. - Supply-chain surprises (malicious or merely hostile-to-your-policy dependencies) land in an environment you can stop, delete, and recreate from the golden without reinstalling your laptop profile.
- Secrets and tokens can be minimized on the golden and injected per clone so a long-lived “pet” VM does not accumulate every experiment you ever ran.
None of this replaces code review, network egress controls, or secret stores—it moves the default place where risky work runs.
Golden disk plus clones
The repository’s layout (as of this writing) centers on templates/golden-devbox.yaml, which extends a bare Ubuntu golden and runs first-boot provision steps (packages, shell), and templates/devbox.yml, which points Lima at ~/.lima/golden-devbox/disk and pins cpus, memory, and disk for ephemeral clones—in the checked-in recipe, disk is larger than the golden base image (20GiB vs 10GiB) so routine work has headroom. The clone template sets vmType: vz (Lima’s Virtualization.framework backend on macOS) and exposes ssh.localPort for stable host-side ~/.ssh/config wiring.
Operational loop from the project’s QUICKSTART:
limactl createthe golden fromgolden-devbox.yaml, start it, then runcloud-init clean --logson the golden (see QUICKSTART for the exactlimactl shell/sudo invocation) so clone copies get a fresh cloud-init—otherwise clones can miss user-data and break Lima-managed SSH.- Stop the golden; its base disk is the artifact you clone from.
limactl createa devbox instance fromdevbox.yml, start it, work, stop or delete when done.
Treat golden disks as build artifacts: when base packages change, refresh the golden deliberately rather than letting every clone diverge.
Cloud-init and Lima provision (system scripts)
The Ubuntu cloud image includes cloud-init, which handles much of the usual “first boot” story on Linux VMs. Lima layers its own metadata on top so instances get consistent SSH access and user setup; that interaction is why the QUICKSTART insists on cloud-init clean --logs on the golden after you bake it—clone disks copied from a dirty golden can skip user-data and leave Lima-managed SSH in a bad state.
Separately, these templates use Lima’s provision field with mode: system: shell scripts under templates/provision/ run as root during initial provisioning (the repo ships packages.sh, fish.sh as examples). Think of them as your repeatable “install baseline tooling” step—conceptually adjacent to cloud-init runcmd/write_files, but entirely under your YAML so you can version and review them like application code.
You should customize them. Swap package lists, add compilers or language runtimes, replace Fish with another login shell, split scripts per concern, or add more provision entries in golden-devbox.yaml. Adjust golden-ubuntu-bare.yaml when CPU, RAM, disk, or vmType should match a different machine class. The defaults exist to document the pattern, not to prescribe one toolchain.
SSH, agent forwarding, and multiplexing
The QUICKSTART ships an OpenSSH pattern: Include ~/.lima/<instance>/ssh.config, ForwardAgent yes, and—critically—ControlMaster no on the Host stanzas you use before the Include, because multiplexed sessions often leave SSH_AUTH_SOCK broken inside the guest even when the agent works on the host.
IdentityAgent points at whichever agent socket you use (plain OpenSSH, 1Password, Secretive, etc.); keys must already be loaded (ssh-add on macOS as needed). The templates do not magically bypass your org’s key-management policy—they show a common wiring that keeps private key material off the guest filesystem while still using Git over SSH.
Secrets at create time
The sample devbox.yml includes a provision entry with mode: data: a small secret.txt on the host is copied into the guest at {{.Home}}/dev-ore-devbox-secret.example (mode 0600) when the instance is created. Changing that file requires delete and recreate—by design, so you know exactly when bytes crossed the boundary.
Prefer not baking long-lived secrets into the golden cloud-init unless you accept they live in the disk image until you rebuild. The README calls out gitignore for real payload files on the host.
Snippets
These blocks mirror the project QUICKSTART.md in dev-ore-devbox; run limactl from the cloned repository root (paths are relative to the YAML under templates/).
Golden (templates/golden-devbox.yaml)
Copylimactl create -y --name golden-devbox ./templates/golden-devbox.yaml
After the bake finishes, reset cloud-init on the golden so clones do not inherit a stale first boot (Permission denied (publickey) or hangs are common otherwise—also check ~/.lima/<instance>/ha.stderr.log):
Copylimactl start golden-devbox limactl shell golden-devbox -- sudo cloud-init clean --logs limactl stop golden-devbox
Clone (templates/devbox.yml)
Copylimactl create -y --name devbox ./templates/devbox.yml limactl start devbox
Copylimactl stop devbox
Copylimactl delete devbox # recreate after changing mode:data files
mode: data reminder
At create, Lima copies file: → path: (paths relative to the YAML file; .. segments are forbidden; {{.Home}} / {{.User}} expand). Example in devbox.yml: templates/provision/secret.txt into {{.Home}}/dev-ore-devbox-secret.example (0600). Gitignore host-side secrets and delete → create whenever that payload changes—no in-place drift.
SSH (~/.ssh/config or config.d)
Set ControlMaster no on the Host entries you merge above Lima’s Include, keep Include ~/.lima/devbox/ssh.config, and align Port / User with your instance (templates/devbox.yml ssh.localPort; User matches limactl show-ssh devbox):
CopyHost lima-devbox devbox ControlMaster no Include ~/.lima/devbox/ssh.config Host devbox HostName 127.0.0.1 Port 60225 # match templates/devbox.yml ssh.localPort User YOUR_GUEST_USER # e.g. limactl show-ssh devbox IdentityFile ~/.lima/_config/user IdentitiesOnly yes IdentityAgent "~/.1password/agent.sock" # example; omit for default ssh-agent StrictHostKeyChecking no UserKnownHostsFile /dev/null ForwardAgent yes
Agent. Load identities on the host first; ssh-add -l should match inside the guest. macOS Apple Keychain example:
Copyssh-add --apple-use-keychain ~/.ssh/id_ed25519 ssh-add -l
Smoke-test Git SSH from inside the clone:
Copyssh -T [email protected]
Multiplexing. If ControlMaster auto from another config leaves SSH_AUTH_SOCK wrong in the guest, kill stale masters (adjust ControlPath if yours differs) before reconnecting:
Copyssh -S ~/.lima/devbox/ssh.sock -O exit
What this does not promise
- Lima is not Firecracker-for-laptops. For API-driven microVM churn on Linux/KVM, see Firecracker microVMs; for the taxonomy comparison, see Lima VMs vs Firecracker.
- Guest compromise with sloppy sharing (wide directory mounts, forwarded ports left open) can still hurt host-adjacent workflows—directory sharing is convenience with a threat-model cost.
- Templates are opinions: ports, users,
vmType, and resource sizes need to match your machine and policy.
Conclusion
dev-ore devbox documents a repeatable Lima pattern: one golden Linux guest, many replaceable clones, SSH agent forwarding for Git, and explicit per-create file injection for secrets—aimed at engineers who want kernel-separated dev sandboxes without giving up host editor ergonomics. Clone sources and docs live on Codeberg: codeberg.org/dev-ore/dev-ore-devbox.
If you are standardizing agent terminals or YAML workflows on top of that boundary, the same toolchain posts still apply—Pi, Archon, Varlock, cmux, and Terax—they decide how you drive the shell; Lima decides which kernel runs it.