Version managers scope the runtime, not everything else

nvm, pyenv and rbenv solve one problem well: which language runtime a project uses. They don't touch the rest of what differs between projects: global npm packages installed once and forgotten, a system library one project needed that now shadows the version another expects, Homebrew formulae that have drifted, a stray variable in ~/.zshrc that only one build depends on. "It works on my machine" really means "it works on my machine today". Six months and a dozen projects later, it's a different machine from the one the setup instructions were written for.

What one workspace per project looks like

Create a Linux workspace for the project, from a cloud image if you want it running in a minute, with the Code & Build profile: 4 CPU cores, 8 GB of memory and 100 GB of storage, sized for a real toolchain rather than a minimal test runner. The habit is simple: one workspace per project, not one you reuse for everything and only half remember the history of.

The split that makes it work day to day: your project files, the ones with Git history that your editor has open, live in a shared folder on your Mac, mounted into the guest with the mount -t virtiofs command the app shows for that folder. The OS, the compilers, the system packages and everything apt install touches stay in the guest. Edit with VS Code Remote-SSH connected to the workspace and it feels like working locally, except that a broken system package or a dependency you regret stays inside a VM you can throw away, not on the Mac you use for everything.

Matching a teammate's setup exactly

A README's setup steps are a description someone has to interpret correctly: which package manager version, which system libraries, which order. A saved base image is the disk itself. Set up a project's environment once, save it, and everyone clones the identical toolchain instead of rebuilding something close to it. The base image versioning post covers naming, which matters because the app doesn't track versions between images: a name like ubuntu-24.04-project-x-node22-2026.09 is the only record of what's in it. You can hand that image to a teammate, too. Settings › Images › Base Images can export it to a disk file, a real copy of the environment rather than a shared cloud link, to move to another Mac or archive.

Trying a new framework without touching your Mac

The smaller, more frequent version: evaluating a library or framework doesn't need a named project, just a workspace you're happy to throw away. Clone a disposable workspace from your usual base image, follow the tool's quickstart (npm create, a new interpreter, a system package you may not keep), and close it when you've made up your mind. None of it touches your Mac's npm cache, Homebrew or any lockfile you'd have to remember to revert. Clones are copy-on-write, so starting one takes seconds.

When a VM is right, and when it's overkill

In the spirit of the Docker Desktop and OrbStack comparison: for per-project dependency isolation on one OS, a container is often lighter and quicker than a VM, and a good version manager plus Docker Compose covers a lot of the same ground. A full Linux VM earns its overhead when you want the isolation to include the OS itself (a separate kernel and system package set) or want root-level changes to be as disposable as the project's files. If your pain is "node_modules is a mess", a container fixes it more cheaply. If it's "I no longer trust what's installed system-wide on my Mac", this workflow is for you.

What to do

  • One workspace per project, not one long-lived workspace whose history you stop trusting.
  • Keep project files in a shared folder, so the guest holds the toolchain and system state, not the only copy of your work.
  • Save a base image once a project's setup is right, and give teammates the exported disk rather than a setup doc to reinterpret.
  • Reach for a disposable workspace, not your Mac, whenever you're trying something you may not keep.

Related reading: a naming convention for base images, one workspace per Git branch for the finer-grained version of this habit, the same pattern for QA and DevOps, and when a container fits better. Or download Velo Workspaces and try it yourself.