The framework's allowed range isn't a recommendation

Velo clamps whatever memory size you request into VZVirtualMachineConfiguration.minimumAllowedMemorySize and .maximumAllowedMemorySize — verified directly against the source. That range exists to keep a configuration valid, not to suggest what's actually a good idea; the framework will happily accept a memory size the host can't comfortably spare, and a workspace maxed out against that ceiling on a Mac that also needs RAM for everything else running competes with the host the same way an overcommitted process would.

What Velo's own defaults imply

Velo's built-in workload profiles pair memory with vCPU count deliberately, and the pairing itself is informative: Study & Office defaults to 4GB, Code & Build to 8GB, DevOps Lab to 12GB, and AI Sandbox — the profile expected to also run a local model or an agent workload — to 16GB. None of these are guesses; they're calibrated to what each workload class realistically needs headroom for, and they're a reasonable starting point to adjust from rather than a fixed rule. A plain shell + editor session rarely needs more than the Study & Office tier; a real build with a dependency-heavy install step benefits from the Code & Build tier's extra headroom for compiler and linker memory use, which spikes well above steady-state usage during the build itself, not just at idle.

The memory balloon device: what "assigned" doesn't mean "locked away"

Every Linux workspace gets a VZVirtioTraditionalMemoryBalloonDeviceConfiguration — a virtio balloon device, verified against Velo's source. The balloon exists specifically so that memory you've assigned to a VM isn't necessarily memory permanently unavailable to the host: it lets the hypervisor reclaim memory the guest currently isn't using, and give it back later if the guest needs it again, with the guest's own kernel cooperating through its virtio-balloon driver rather than the reclaim happening behind its back. This is standard Linux and standard Virtualization.framework behavior, not something Velo tunes with custom policy — the device is created with its defaults, and the framework and guest kernel handle the actual inflate/deflate behavior between them.

The practical implication is worth being precise about rather than overstating: the balloon device means a generously-sized workspace doesn't necessarily starve the host the way a naive, non-reclaimable allocation would if the guest is mostly idle — but it's not a substitute for reasonable sizing, and how aggressively memory actually gets reclaimed depends on framework and guest-kernel behavior neither this post nor Velo's own code controls in detail. Size for what the workload needs; don't treat the balloon device as a reason sizing doesn't matter.

A sizing approach that isn't guessing

  1. Start from the closest workload profile above rather than an arbitrary number.
  2. Watch for swap activity inside the guest during the actual workload, not at idle — free -h or vmstat showing swap in use under real load is the clearest sign a workspace is undersized, more reliable than watching the number look "full."
  3. Size to the peak, not the average — a build's linker step or a test suite's worst moment matters more than its steady-state footprint, the same lesson the LLM RAM math post makes for KV cache growth during a long session.
  4. Leave real headroom on the host — macOS itself, plus whatever else is running, needs its own share of unified memory; a workspace sized to consume everything technically available leaves nothing for that.

Related reading: how much RAM local LLMs actually need, a genuinely different and more calculable question, how vCPU core allocation works, and how many workspaces you can realistically run at once, which this post's sizing approach feeds directly into. Or download Velo Workspaces and try it yourself.