dev-ore devbox: golden Lima VMs and disposable clones for safer coding

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 | sh installers, 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.

flowchart TB subgraph bake["Bake golden once"] GA["golden-devbox.yaml + golden-ubuntu-bare.yaml"] PS["provision mode system scripts"] GV["golden-devbox guest"] CL["cloud-init clean --logs then stop"] GD[("~/.lima/golden-devbox/disk")] GA --> GV PS --> GV GV --> CL CL --> GD end subgraph dx["Ephemeral developer sessions"] DV["devbox.yml limactl create"] DATA["mode data copies at create"] CV["devbox guest"] DD[("~/.lima/devbox/disk")] NX["stop or delete devbox"] GD --> DV DV --> DATA DATA --> CV CV --> DD CV --> NX NX --> DV end HOST["Host editor + ssh-agent"] -->|"SSH ForwardAgent"| CV
sequenceDiagram autonumber participant H as Developer host participant L as limactl participant G as golden-devbox guest participant C as devbox clone Note over H,G: Bake and freeze base disk H->>L: create + start golden-devbox L->>G: cloud-init + Lima provision scripts H->>G: sudo cloud-init clean --logs H->>L: stop golden-devbox Note over H,C: Disposable DX loop loop Each session H->>L: create devbox from devbox.yml L->>C: copy from golden disk + mode data H->>L: start devbox H->>C: SSH work isolated kernel H->>L: stop or delete devbox end

Operational loop from the project’s QUICKSTART:

  1. limactl create the golden from golden-devbox.yaml, start it, then run cloud-init clean --logs on the golden (see QUICKSTART for the exact limactl shell/sudo invocation) so clone copies get a fresh cloud-init—otherwise clones can miss user-data and break Lima-managed SSH.
  2. Stop the golden; its base disk is the artifact you clone from.
  3. limactl create a devbox instance from devbox.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

flowchart LR subgraph Host["Host"] ED["Editor terminal"] AG["ssh-agent or IdentityAgent"] LM["Lima SSH glue"] end subgraph Guest["Clone Linux VM"] SH["Shell toolchain agents"] end ED --> LM LM -->|"SSH"| SH AG -.->|"ForwardAgent"| SH

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)

Copy
limactl 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):

Copy
limactl start golden-devbox limactl shell golden-devbox -- sudo cloud-init clean --logs limactl stop golden-devbox

Clone (templates/devbox.yml)

Copy
limactl create -y --name devbox ./templates/devbox.yml limactl start devbox
Copy
limactl stop devbox
Copy
limactl 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):

Copy
Host 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:

Copy
ssh-add --apple-use-keychain ~/.ssh/id_ed25519 ssh-add -l

Smoke-test Git SSH from inside the clone:

Copy
ssh -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:

Copy
ssh -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.

Featured Posts