Two jobs, one root cause

QA and DevOps rarely get grouped together, but a meaningful share of the bugs each field fights are the same bug wearing a different name.

On the QA side, it's the classic flaky test: it passes alone, and fails only when it runs after some other test. That's a recognized category, not a mystery — when one test leaves a record changed, a temp file behind, or a session open, the next test inherits that state and behaves differently than it would against a genuinely clean setup. The 2026 Flaky Test Benchmark Report puts timing issues, test data problems, and runtime errors at over 70% of flaky failures combined — categories that are, in large part, downstream of environment state that wasn't actually reset the way the test suite assumed.

On the DevOps side, it's the install script or Ansible playbook that passes validation and then breaks on a truly fresh host. Configuration management tools are built around idempotency by design — running the same script twice should converge to the same state — but idempotency is a property you have to actually test, by running the script against arbitrary starting states, not just assume because the tool promises it. If the only place a deploy script ever runs before production is a staging box that's accumulated six months of manual fixes nobody wrote down, "it passed staging" isn't evidence the script is idempotent. It's evidence it works against that one specific, undocumented, no-longer-reproducible machine.

"Reset" and "disposable" are different guarantees

The standard fix for both is some version of a teardown script: a test fixture that rolls back the database, a CI step that runs vagrant destroy && vagrant up, a habit of SSHing in and manually undoing whatever the last run changed. All of these work, most of the time — and "most of the time" is exactly the problem, because a teardown script is still code, written by someone (possibly you, months ago) who had to correctly anticipate every piece of state a run could leave behind. Miss one thing — a cron job that fired mid-test, a package installed as a side effect, a config file the installer half-wrote before failing — and the next run doesn't start clean. It starts probably clean, which is a different and much weaker guarantee.

A disposable VM sidesteps that category of bug rather than trying to write a teardown script good enough to catch every case: there's no cleanup step to get wrong, because nothing about the workspace outlives it. The VM — and everything it accumulated during the run — is gone the moment you close it. The next run doesn't start from "whatever the teardown script managed to undo." It starts from the same sealed image, bit for bit, every time.

What this looks like for QA: base image once, disposable workspace per run

Seal a fully configured environment — OS, browser or test dependencies, the application under test — into a base image once. Every subsequent test run clones a new, independent Disposable Workspace from it in seconds using copy-on-write, runs, and is thrown away the moment you close it — no snapshot to remember to delete, no "did I leave something installed from last run" doubt eating into your confidence in a pass. When a test does fail, you've eliminated an entire category of noise: since every run starts from the identical known-good state, a failure that reproduces is a real failure, and a failure that doesn't reproduce against the same base image is much more likely to be a genuine race condition than leftover state from three runs ago. That's a meaningfully easier bug to triage than "did something about the environment change."

What this looks like for DevOps: test against provably fresh state, not a machine you half-remember fixing

The same base-image-plus-disposable-workspace pattern applies directly to validating a deployment script, an install procedure, or a config change before it ships — clone a fresh workspace from a vanilla base image (an official Ubuntu Server image, for instance) every time you test the script, rather than reusing a box you've SSH'd into and patched by hand at some point. If the script only works because that machine already had a dependency installed from a previous test, a truly fresh clone is what catches it — before a production host does.

One specific, common failure mode is worth calling out: an install script that goes wrong early enough that networking or SSH itself never comes up. That's exactly the situation where SSH-based remote-debugging tools can't help you, because the thing that's broken is SSH. A built-in serial console — a terminal into the guest that doesn't depend on the network stack having come up at all — is the difference between staring at a workspace that's silently stuck and actually seeing why. Once a script passes cleanly against a fresh clone, seal that state as a new base image and version it the same deliberate way you version the script itself, rather than letting "the golden image" mean a Slack thread about which server it was based on.

Where this sits next to Vagrant, Multipass, and Lima

Worth being straightforward about this: Vagrant, Canonical's Multipass, and Lima are all real, respected, free tools, and any of them is a reasonable answer to "I want a Linux VM on my Mac for testing." Multipass and Lima in particular work well on Apple Silicon today, and plenty of people have landed on Lima specifically after finding Vagrant's Apple Silicon story rougher going. None of that is in question.

The difference is what's the default versus what you have to build yourself. Those tools give you a VM; disposability, base-image versioning, a serial console, and generated SSH/VS Code Remote config are workflow you script on top, if you want them, using a Vagrantfile or a Lima YAML config and your own conventions. Velo Workspaces starts from the assumption that the disposable-by-default shape is the workflow, not an extra layer: close the workspace and it's gone, seal a base image in one click and clone from it with copy-on-write, and the serial console and SSH/VS Code Remote config are there without being wired up by hand. If you're already happy maintaining that scripting yourself, you may not need to switch. If you're the one who'd have to write that scripting because nothing gave it to you by default, that's the actual gap here.

What to actually do

  • Don't trust a teardown script to fully undo a run. It only catches what its author thought to anticipate — disposability removes the category of bug rather than trying to patch every case.
  • Test idempotent scripts against provably fresh state, not a machine that's "probably" been reset — a staging box with an undocumented history isn't evidence of anything.
  • Keep a path to the guest that doesn't depend on networking, for the specific and common case where the bug is why the network stack itself didn't come up.
  • Version your base/golden image as deliberately as you version the script that builds it — "which server was this based on" shouldn't be a question you answer from memory.

This same disposable-by-default idea shows up in our security deep dive too, for a different reason — a compromised AI agent's changes not outliving the session is the same property that makes a flaky test's leftover state not outlive its run. If Ubuntu Server is your base image of choice, see what's actually different about running it on Apple Silicon, including a cloning gotcha worth knowing about in advance. See the QA and DevOps profiles on the homepage, or download Velo Workspaces and try it yourself.