MicroVMs vs containers vs VMs vs binaries vs bare metal
There is no best way to run software, only trade-offs between isolation, start time, memory, cost and compatibility. This page compares eight common ways to run a workload, for developers, vibe coders, AI-agent builders and CI/CD engineers.
Short answer
- Trusted code on a machine you control: plain processes or containers.
- Untrusted code or many short-lived isolated environments: microVMs.
- Single-purpose services that idle most of the time: unikernel-based microVMs.
- Rare, event-driven code: serverless functions. Tiny sandboxed plug-ins: WebAssembly.
- Durable state, mixed operating systems: virtual machines. Full hardware, GPUs: bare metal.
Ratings below are qualitative. Where a figure is given it is a typical range that is well established in general, not a benchmark, and it varies with hardware, image size, runtime and configuration. Measured figures for MicroSaaS.app are in Where MicroSaaS.app fits and on the methodology page.
What is each way of running software?
Bare metal
Your software runs directly on a physical server with no virtualisation layer. You own the whole machine, its kernel and its hardware.
Benefits
- Full access to every CPU feature, device and GPU.
- No virtualisation overhead and no neighbours competing for the machine.
- The simplest security model when the machine is yours alone.
Disadvantages
- Slow to provision, usually minutes to days, and you pay for the whole machine whether you use it or not.
- You patch the operating system, firmware and hardware yourself.
- Poor density: one failure domain per box, and little isolation inside it unless you add another layer.
When to pick it. Pick it for GPU training, latency-critical or hardware-bound work, large steady databases and anything that needs the full machine.
Traditional virtual machines
A hypervisor splits a physical host into virtual machines, each with its own full operating system and virtual hardware. The boundary between guests is enforced by hardware virtualisation.
Benefits
- Strong isolation and a familiar model: any operating system, any software.
- Persistent virtual disks, snapshots and live migration on most platforms.
- Mature tooling for backup, networking and compliance.
Disadvantages
- Each guest carries a full operating system, so idle memory and boot time are high.
- You patch every guest operating system.
- Poor fit for short-lived or scale-to-zero work.
When to pick it. Pick it for long-lived services, mixed operating systems, lift-and-shift of existing software and workloads where isolation and persistence matter more than start time.
Containers
A container packages a program and its libraries and runs it as an isolated process on the host kernel, separated by namespaces and resource limits. It starts fast and packs densely because nothing boots a separate kernel.
Benefits
- Fast start and high density on one host.
- A huge ecosystem of images, registries and orchestrators.
- Same image from laptop to production.
Disadvantages
- All containers on a host share one kernel, so a kernel bug can cross the boundary.
- Image pulls and orchestrator overhead dominate cold starts.
- Container isolation alone is a weak fit for untrusted code.
When to pick it. Pick it for your own trusted services, CI jobs, and anything where a shared kernel is acceptable and an ecosystem of tooling matters.
MicroVMs
A microVM is a virtual machine stripped to the minimum: a small guest kernel, few virtual devices and no legacy hardware. It keeps the hardware isolation of a virtual machine while booting far faster and using far less memory.
Benefits
- Hardware-level isolation with a small attack surface.
- Boots in a fraction of a second, so scale to zero and wake on request are practical.
- Runs a normal Linux guest, so most software runs unchanged.
Disadvantages
- Fewer devices than a full virtual machine, and usually no GPU.
- Self-hosting needs real engineering: images, networking and a scheduler.
- Each instance still carries a guest kernel, so memory overhead is above a container.
When to pick it. Pick it when you run untrusted or multi-tenant code, or need many short-lived isolated environments, and a shared kernel is not acceptable.
Unikernel-based microVMs
A unikernel links your application with only the operating-system pieces it needs into one small image that runs as a single-purpose microVM. There is no shell, no user accounts and no general-purpose userland.
Benefits
- Smallest guest: very low idle memory and boot times that can be a few milliseconds.
- Very small attack surface inside the guest, with hardware isolation outside it.
- Very high density and a good match for scale to zero.
Disadvantages
- Not every program runs. Software that needs fork, many processes or unusual kernel features may need changes or may not run at all.
- Harder to debug: no shell and limited tooling inside the guest.
- A younger ecosystem with fewer ready-made images.
When to pick it. Pick it for single-purpose network services, such as a database, proxy or queue, that start often, idle most of the time and need strong isolation.
Plain binaries and processes (systemd)
You copy a binary onto a machine and let the init system, usually systemd, start and supervise it. All processes share the host operating system, with isolation limited to users, permissions and optional sandboxing options.
Benefits
- The lowest overhead and the simplest thing that can work.
- Everything is visible with standard tools: logs, top, strace, a shell.
- Cheapest at small scale, since one machine can run many services.
Disadvantages
- Weak isolation: a compromised process threatens the whole host.
- Dependency conflicts and manual deployment unless you add tooling.
- No scale to zero beyond what socket activation gives you, and the host is always on.
When to pick it. Pick it for a few trusted services on a machine you control, internal tools, and anything where simplicity beats isolation.
Serverless functions
You upload a function and the provider runs it on demand, scaling copies up and down with requests, including to zero. You do not manage servers, but you accept the provider's runtime, time limits and packaging rules.
Benefits
- No servers to run, and a natural fit for spiky or rare traffic.
- Scale to zero and per-invocation billing are built in.
- Low operational effort for small event-driven code.
Disadvantages
- Stateless by design, so state lives in other services.
- Cold starts vary with runtime and package size, and limits on duration, memory and system access apply.
- Hard to debug locally, and tied to the provider's model, which makes moving harder.
When to pick it. Pick it for event handlers, webhooks, glue code and APIs with irregular traffic, where you want to run nothing yourself.
WebAssembly runtimes
A WebAssembly runtime executes portable compiled modules inside a memory-safe sandbox in the host process. Modules get only the capabilities the host explicitly grants.
Benefits
- Very fast start, often well under a millisecond to a few milliseconds, and tiny memory use.
- One portable binary across operating systems and CPU architectures.
- Fine-grained, capability-based permissions.
Disadvantages
- Limited system access and threading, networking and file APIs depend on the host runtime.
- Language and library support varies, and many existing programs need porting.
- Security rests on the runtime being correct, and the tooling is younger.
When to pick it. Pick it for plugins, extension points, edge functions and untrusted snippets that must start instantly and run inside another program.
How do they compare side by side?
On a narrow screen, scroll the table sideways inside its frame. The first column stays in view.
| Criterion | Bare metal | Virtual machine | Container | MicroVM | Unikernel microVM | Process on a shared host | Serverless function | WebAssembly runtime |
|---|---|---|---|---|---|---|---|---|
| Isolation strength | Strongest when the machine is yours alone | Strong: hardware virtualisation boundary | Moderate: namespaces on a shared kernel | Strong: hardware virtualisation, minimal devices | Strong: hardware boundary plus a tiny guest | Weak: users and permissions only | Depends on the provider, often container or microVM based | Strong in-process sandbox, but relies on the runtime being correct |
| Cold start | Minutes or longer to provision | Typically seconds to a few minutes | Typically under a second to a few seconds; image pulls add time | Typically well under a second | Typically milliseconds to tens of milliseconds | Milliseconds once installed; usually always on | Typically under 100 ms to over a second, varies by runtime and traffic | Typically microseconds to milliseconds |
| Idle memory overhead | A whole operating system: hundreds of MB or more | A guest operating system: typically hundreds of MB to over 1 GB | Low: megabytes beyond the program itself | Guest kernel plus program: typically tens of MB, with a few MB of monitor overhead | Lowest of the virtual machines: a few to tens of MB | Lowest: the program only | None for you while scaled to zero; provider-managed | Low: megabytes |
| Density (instances per host) | One workload per machine | Tens to low hundreds per host, limited by memory | Hundreds or more | Hundreds or more, limited by memory | Many more than a typical virtual machine, limited by memory | As many as the operating system can schedule | Provider's concern; not your choice | Thousands |
| Cost at small scale | Highest: you pay for the whole machine | Moderate: minimum instance size applies | Low on a shared host | Low when idle instances scale to zero | Low when idle instances scale to zero | Lowest: one cheap machine runs many | Very low at low traffic, rises with sustained load | Low |
| Scale-to-zero friendliness | Poor | Poor | Possible with an orchestrator; cold starts cost seconds | Good | Excellent | Not applicable; the host stays on | Native | Good |
| Persistence and state | Full local disks | Persistent virtual disks and snapshots | Ephemeral layer; volumes for state | Depends on setup; volumes can be attached | Small volume or none; usually disposable | The host filesystem | Stateless; state lives elsewhere | Stateless by default; host-provided storage |
| Hardware or kernel feature access | Everything, including GPUs | What the hypervisor exposes; GPU passthrough possible | Host kernel features; devices can be passed in | Limited device set; usually no GPU | Only what the unikernel implements; no GPU | Everything on the host | Very limited | Only what the host runtime grants |
| Portability and compatibility (what runs unchanged) | Anything built for that hardware | Any operating system image for the architecture | Linux userland images | Most Linux software | Programs the compatibility layer supports; fork-heavy software may not run | Software built for that distribution and architecture | Supported language runtimes within limits | Modules compiled to WebAssembly; many programs need porting |
| Operational complexity | Highest | High: every guest to patch | Moderate, more with an orchestrator | Moderate to high self-hosted; low as a managed service | High to build yourself; low as a managed service | Low on one host | Low to run, harder to test and debug | Moderate; younger ecosystem |
| Attack surface and shared-kernel risk | The full operating system | Hypervisor plus guest operating system | Shared host kernel is the main risk | Small hypervisor and device model | Smallest guest, hardware isolation outside | Everything shared with the host | Managed by the provider | Runtime bugs; a sandbox escape needs a runtime flaw |
| Debuggability | Best: everything available | Good: shell and full tools | Good: exec, logs, standard tools | Good with a full Linux guest | Harder: no shell, limited tools | Best: standard host tools | Hard: logs, differing local emulation | Improving; fewer tools |
| Best-fit workloads | GPU, latency-critical, large steady databases | Long-lived services, mixed operating systems | Trusted services, CI jobs, general apps | Untrusted code, multi-tenant, short-lived environments | Single-purpose network services that idle most of the time | A few trusted services on one machine | Event handlers, webhooks, spiky APIs | Plugins, edge functions, embedded untrusted logic |
Where MicroSaaS.app fits
MicroSaaS.app runs each service in the unikernel-based microVM category. This is what we do and have measured, and where it stops.
What we do
- Each service runs in its own microVM, which boots in milliseconds.
- Measured wake on request is about 30 ms on the platform side and about 270 ms end to end, measured from a laptop with a network round trip of about 240 ms. See how we measured it.
- Services idle down to zero and wake on traffic.
- Catalogue services run in 64 to 128 MB. See the services and their memory floors.
- Services are disposable and carry a time to live. See pricing for the maximum on each plan.
- There is hardware-level isolation between a service and the host.
Where it stops
- Services of different customers share a private network, so each service's own credentials are what separate customers. The terms say so.
- A compatibility layer runs your program, so programs that need fork, many processes or unusual kernel features may not run.
- There is no GPU.
- State is disposable by design. Do not keep data here that you cannot recreate.
- There is no availability guarantee.
Create services with the command line tool, the REST API or the MCP server. The docs show how, and CI testing and AI agents are the common starting points.
Which should I choose for my situation?
| Situation | Pick | Why |
|---|---|---|
| A throwaway dependency for a CI run, such as a bucket, database or mail inbox | Unikernel-based microVM or a container | It must start quickly, cost almost nothing and disappear. A container is fine if the job is trusted; a microVM service avoids running it yourself. |
| A sandbox for an AI agent that runs generated code or creates its own infrastructure | MicroVM | Generated code is untrusted, so hardware isolation matters more than the last bit of density. |
| A long-lived production database | Virtual machine or bare metal | Durable disks, predictable performance and backups matter more than start time. Disposable services are the wrong place for data you cannot lose. |
| GPU training or inference | Bare metal, or a virtual machine with GPU passthrough | Needs direct device access. MicroVM and unikernel services here have no GPU. |
| Running untrusted code from other people | MicroVM, or a WebAssembly runtime for small plug-in logic | A shared kernel is the risk. Use a hardware boundary, or a memory-safe sandbox with narrowly granted capabilities. |
| A latency-critical library running inside your own process | Plain process or bare metal; WebAssembly if you need a sandbox | Crossing a virtual machine or container boundary adds cost you cannot afford on the hot path. |
| A webhook handler with rare, irregular traffic | Serverless function | Nothing to run and no cost while idle. Accept the provider's limits and cold starts. |
| A few trusted internal services on one box | Plain processes under systemd, or containers | The simplest thing that works. Add isolation only when the threat model asks for it. |
Questions about runtimes
What is the difference between a microVM and a container?
A container shares the host kernel; a microVM boots its own small kernel behind a hardware virtualisation boundary. That makes microVMs a stronger boundary for untrusted code, at the cost of a little more memory per instance and fewer devices.
Are microVMs slower than containers?
They typically start somewhat slower and use more memory, but both are fast enough for most uses. Containers usually start in under a second to a few seconds once the image is present. MicroVMs typically boot in well under a second, and unikernel-based microVMs can boot in milliseconds. Figures vary with the image, host and runtime.
What is a unikernel?
A unikernel is an application linked with only the operating-system code it needs, run as a single-purpose virtual machine. It has no shell and no general userland, which keeps it small and fast to start, and limits what software can run on it.
When should I use bare metal instead of a microVM?
Use bare metal when you need the whole machine. That means GPUs, direct hardware access, very large steady workloads and latency that cannot tolerate any virtualisation layer.
Can WebAssembly replace containers?
Not in general. WebAssembly suits small, fast, sandboxed modules such as plugins and edge functions. Many existing programs depend on system features that a WebAssembly runtime does not provide, so they need porting.
Is a container enough to run untrusted code?
Usually not on its own. Containers share the host kernel, so a kernel flaw can cross the boundary. For untrusted code, add a hardware boundary such as a microVM or a sandboxed runtime.
Which option is cheapest at small scale?
Plain processes on one machine are cheapest, then containers, then services that scale to zero. Serverless functions and scale-to-zero microVMs cost almost nothing while idle but can cost more under sustained load. Bare metal and virtual machines cost the same whether used or not.
Do services on MicroSaaS.app run in containers?
No. Each service runs in its own microVM. Services boot in milliseconds, idle down to zero and wake on traffic. Services of different accounts share a private network, so each service's own credentials are what separate accounts.
Related: microVM and scale to zero in the glossary.
Last updated