What --platform linux/amd64 actually triggers
Ask for an architecture your machine doesn't have — docker pull --platform linux/amd64 <image>, or an image with no arm64 build in its manifest at all — and Docker doesn't refuse or silently swap in the wrong architecture. It runs the amd64 binaries through QEMU's user-mode emulation, registered with the kernel via binfmt_misc the same way Linux dispatches any non-native executable format. It works, genuinely, but it's full software instruction translation with no hardware acceleration behind it — noticeably slower for anything CPU-bound than the same container running its native arm64 build, in the same way this blog's Virtualization.framework vs. QEMU post describes TCG emulation generally.
Docker Desktop sets this QEMU registration up for you automatically the first time it's needed. Running Docker natively inside a Linux VM — which is what happens when Docker Engine runs directly inside a Velo workspace rather than through Docker Desktop on the host — that registration doesn't exist until something puts it there, usually a one-time docker run --privileged --rm tonistiigi/binfmt --install all inside the guest.
A faster path, if you already have Rosetta set up
Here's the part worth knowing if you're running Docker inside a Velo Linux VM specifically: setting up Rosetta for Linux registers Rosetta as the guest kernel's binfmt_misc handler for x86_64 ELF binaries, using the --fix-binary yes flag. That flag has a consequence beyond what that post covers in depth: it opens the interpreter's file descriptor once, at registration time, which is exactly what lets a binfmt_misc registration keep working for a process running inside a container's own mount namespace — the container image doesn't need to carry a copy of the interpreter itself. That's standard, documented Linux kernel behavior, not something specific to Velo, and it's the same principle tonistiigi/binfmt relies on for QEMU.
The practical implication: if Rosetta for Linux is already registered in a guest per that post's setup steps, an amd64 container's x86_64 binaries may already be routing through Rosetta's translation rather than falling back to QEMU emulation — worth testing directly rather than assuming, since this isn't something Velo explicitly wires together or has published a benchmark for. Pull a known amd64-only image and time something CPU-bound inside it before and after running the Rosetta setup steps in that same guest; if Rosetta is doing the work, the difference should be obvious without needing precise numbers to notice it.
When emulation — of either kind — isn't the right answer
Binary translation, Rosetta's or QEMU's, is a workaround for a missing native build, not a substitute for having one. If it's your own image that's amd64-only, the actual fix is a multi-arch build — docker buildx build --platform linux/amd64,linux/arm64 — so nothing has to be emulated by anyone pulling it, including your own CI. If it's a third-party image and no arm64 build exists at all, emulation is genuinely the only option short of finding an alternative image, and it's worth budgeting for the performance hit rather than being surprised by it mid-build.
Related reading: Rosetta 2 in Linux VMs: what it actually does, can you install x86 Linux on an Apple Silicon Mac for the whole-OS version of this question, and ARM64 vs. x86-64 on Apple Silicon for the multi-arch container basics this post builds on. Or download Velo Workspaces and try it yourself.