← Back to list

Self-Hosted Sandboxes: How to Pick Between Containers and MicroVMs

This guide is for developers who want to roll up their sleeves and build their own sandboxing infrastructure. While managed services offer…

Dafe · 2025-11-15 04:27 · 0 claps · 8.6 min read
#ai-agent #devops #sandbox #docker #aws-firecracker
Open on Medium ↗
Wiki topics: AGT · AI Agents ☁️ · DevOps & Cloud 🥊 · Combat Sports

Self-Hosted Sandboxes: How to Pick Between Containers and MicroVMs

Sand castle with colourful toys on a beach somewhere.

Sand castle with colourful toys on a beach somewhere.

This guide is for developers who want to roll up their sleeves and build their own sandboxing infrastructure. While managed services offer convenience, they can get expensive fast, especially for experiments or prototypes.

This guide focuses on the DIY, self-hosted path as a powerful and cost-effective alternative. Specifically, we’ll dig into the two main options you have today: old-school containers (Docker, Podman) and the new hotness, microVMs.

We’ll walk through:

  • When containers are enough
  • When you must graduate to microVMs
  • A simple path from “works on my machine” to “secure-ish in production”

Let’s dive in.

1. Containers vs. MicroVMs in Plain Language

There’s one key difference:

  • Containers share the host kernel
  • MicroVMs get their own kernel

Everything else is trade-offs.

Containers: The Roommate

Think of a container as a roommate in a shared apartment.

You each get:

  • Your own room → your process tree and filesystem
  • Your own locks → Linux namespaces and cgroups

But you share:

  • The foundation, plumbing, and electrical system → the host OS kernel

That’s the mental model. You’re separated — but not completely.

  • What containers are: A way to package and isolate your app using the host’s kernel
  • Why they’re everywhere: They’re fast, lightweight, and the tooling is world-class (hello, Docker Hub)

In practice:

  • Startup time is usually ~10–50ms
  • Overhead is tiny
  • The ecosystem is enormous

This is why containers are the default for most internal tools, web apps, and microservices.

MicroVMs: The Private Island

Now flip the model.

Instead of roommates in one apartment, imagine giving each tenant their own tiny private island:

  • Each island gets its own power, water, and laws → its own kernel
  • The boundary between islands is enforced by hardware, not just software tricks

That’s a microVM.

  • What microVMs are: Tiny, stripped-down virtual machines
  • What they use: Hardware virtualization to create a hard isolation boundary
  • Why they exist: To safely run untrusted code on shared hardware

Modern examples:

  • AWS Firecracker
  • libkrun-based systems like microsandbox

They:

  • Boot in roughly ~125ms+
  • Use a few MB of RAM per VM
  • Offer real isolation between tenants

Here’s the trade-off in one table:

Table that compares containers to microVMs

Table that compares containers to microVMs

The old story that “VMs are slow and bloated” is outdated. MicroVMs are quickly becoming the default for high-security, multi-tenant code execution.

2. The Real Question: How Much Do You Trust the Code?

When you’re self-hosting, the real decision filter is simple:

How much do you trust the code you’re running?

Everything flows from that.

When Containers Are the Right Choice

Use containers when:

  • You control the code (your own apps, internal tools, CI runners)
  • Your team is the only tenant
  • Raw startup speed matters more than hard isolation

For these workloads, containers are ideal:

  • They’re cheap
  • They’re easy to deploy
  • Every hosting provider and DevOps tool speaks “Docker”

But there’s a catch. Because containers share the host kernel, you’re exposed to:

  1. Kernel exploits (if someone escapes the container)
  2. Misconfiguration (the far more common way people wreck their security)

You can’t eliminate the kernel risk completely, but you can make containers much safer with a few non-negotiables.

Baseline Hardening for Containers

If you’re self-hosting containers, this is the minimum bar:

  1. Run as a non-root user Don’t run your app as root inside the container
  2. Use minimal base images Prefer distroless or alpine to shrink your attack surface
  3. Set tight resource limits Use flags like --memory and --cpus so one container can't starve the host
  4. Drop capabilities by default Start with --cap-drop=ALL Add back only the capabilities your app actually needs
  5. Use a read-only root filesystem Run with --read-only when possible

Here’s what’s interesting:

The biggest danger usually isn’t a brand-new kernel 0-day. It’s:

  • Running containers with --privileged
  • Mounting the Docker socket into a container
  • Exposing more of the host filesystem than you meant to

If that kind of misstep is unacceptable for your threat model, containers are no longer enough. You need stronger isolation.

When You Need MicroVMs Instead

You should seriously consider microVMs when:

  • You’re running **untrusted code from strangers
  • **Public code execution services
  • Multi-tenant SaaS platforms
  • Plugins and extensions that execute user logic
  • You’re building AI agents that can run arbitrary code and you don’t fully trust what they’ll do
  • A compromise of one tenant must not risk the host or other tenants

MicroVMs give each sandbox its own kernel. That’s the key upgrade.

If an attacker:

  • Exploits a bug in the guest kernel
  • Gains full control of the code inside the microVM

They’re still stuck inside that microVM. The host and other tenants are protected by a hardware-enforced boundary.

The trade-offs:

  • More complexity: You need:
  • Hardware virtualization (VT-x or AMD-V)
  • A microVM stack like Firecracker or a libkrun-based system such as microsandbox
  • More overhead than containers: Still lightweight, but not free

If you’re running untrusted workloads, this trade is usually worth it.

3. From Laptop to VPS: A Simple Path

Let’s get practical.

How do you go from:

“I have some code running in a Docker container on my laptop”

to:

“I have a self-hosted sandbox on a remote server”?

Here’s a simple three-step path.

Step 1: Prototype Locally with Containers

Don’t start with microVMs. Start with what you already know: containers.

Locally:

  • Package your workload into a Docker/OCI container image
  • Figure out:
  • What dependencies it needs
  • What files it needs to read/write
  • What ports it needs to expose
  • Iterate until you have a single, reliable docker run command that:
  • Starts your workload
  • Works every time

At this point you’ve:

  • Defined your runtime environment
  • Identified your dependencies
  • Proven the basic sandboxed workflow Crucially, that image is an ordinary OCI/Docker image. You’ll reuse this same image whether you run it directly with Docker on a VPS (Path A) or let microsandbox boot it inside a microVM (Path B).

Only then should you move to a server.

Step 2: Pick a VPS That Won’t Fight You

Next, you need a server.

You’re looking for a VPS (or bare-metal machine) with:

  • Virtualization support (if you plan to use microVMs)
  • Look for nested virtualization / VT-x / AMD-V

Providers like **OVH and [Hetzner](https://www.hetzner.com/cloud/)** are popular because:

  • They’re relatively cheap
  • They give you enough control to run containers and microVMs

Once you’ve picked a provider, spin up a small instance and you’re ready for deployment.

Step 3: Choose Your Deployment Path

On your new server, you now have two clear paths:

  1. Path A: Container-only quick win
  2. Path B: MicroVM-based secure path

Let’s walk through both.

4. Path A — The Container-Only Quick Win

This is the “get something working today” option.

On your VPS:

  1. SSH into the server
  2. Install Docker (or your container runtime of choice)
sudo apt update && sudo apt install docker.io
  1. Pull your image and run it using the same docker run command you perfected locally—now with hardening flags

And you’re done.

You now have:

  • A working sandbox
  • Running in production
  • With minimal friction

The downside:

  • You’re still on the container isolation model
  • You’re still sharing the host kernel

For trusted workloads, this is often enough. For untrusted workloads, it’s a good prototype — but not your final form.

Path A takes the image you built in Step 1 and runs it directly with Docker on your VPS. Path B keeps that same image but changes the isolation boundary: instead of a plain container, microsandbox runs it inside a microVM.

5. Path B — The MicroVM Secure Path (using microsandbox)

Now let’s talk about the more secure path.

We’ll use microsandbox as an example, because:

  • It builds on top of libkrun
  • It’s designed specifically for sandboxed code execution
  • It aims to make microVMs feel as approachable as containers And we’ll assume you’ve already packaged your workload as a Docker/OCI image in Step 1 — microsandbox consumes that same standard image format and runs it inside a microVM instead of a plain container.

Here’s the high-level flow:

  1. Get a virtualization-ready server
  2. Install microsandbox
  3. Run the server securely
  4. Expose it over the network (carefully)
  5. Connect a client and run workloads

Let’s go step by step.

5.1 Provision a Virtualization-Ready Server

First, confirm your machine supports virtualization:

  • Look for Intel VT-x or AMD-V
  • If you’re using a VPS, make sure the provider supports nested virtualization

This is a hard requirement for microVMs. Without it, you can’t use Firecracker, libkrun, or tools built on top of them.

Providers like Hetzner and OVH often support this out of the box — just double-check their docs or ask support.

5.2 Install microsandbox

Once your server is ready, SSH in and install microsandbox:

curl -sSL https://get.microsandbox.dev | sh

This bootstraps the microsandbox tooling onto your machine.

5.3 Start the Server (Without Shooting Yourself in the Foot)

This is where many people make a quiet but serious mistake.

The microsandbox docs often show a development-friendly command:

msb server start --dev

This is great for local testing.

But here’s the catch:

  • --dev relaxes important security defaults
  • It’s not meant for production

For a real deployment:

  • Do not use --dev
  • Start the server without it
  • Use configuration files and flags intended for production

The exact options will evolve over time, so follow the microsandbox docs. The principle stays the same: dev flags are for dev.

5.4 Configure Networking and Remote Access

By default, microsandbox plays it safe:

  • The server listens only on 127.0.0.1
  • Nothing is exposed to the outside world

That’s a good default. But on a VPS, you’ll often want to:

  • Run the server on the host
  • Connect to it from a different machine or app

So you need to do two things.

5.4.1 Server-Side: Expose the Port

First, bind the server to all network interfaces:

# Check the microsandbox docs for the latest flags
# This example uses port 7263 ("SAND" on a phone keypad)
msb server start --host 0.0.0.0 --port 7263

Next, open the port in your firewall. On many Linux servers using ufw:

sudo ufw allow 7263/tcp

If your provider uses security groups or a web UI for firewall rules, add an inbound rule for that port there too.

5.4.2 Client-Side: Point Your App at the Server

Now you need a client to talk to your microsandbox server.

For Python, you might use a PythonSandbox client that:

  • Defaults to a local server
  • Can be configured to talk to your remote VPS

Often you’ll configure the server address via an environment variable:

import asyncio
from microsandbox import PythonSandbox

# Example:
# export MCP_SERVER_URL="http://<your_vps_public_ip>:7263"
async def main():
    # The client connects to the remote microsandbox server
    async with PythonSandbox.create(name="my-remote-app") as sb:
        exec = await sb.run("print('🚀 Secure execution from afar!')")
        print(await exec.output())
asyncio.run(main())

The exact client API may differ, but the pattern is always:

  • Run a server on your VPS
  • Point a client at its URL
  • Execute code inside microVM-backed sandboxes

5.5 Package and Launch Workloads

Here’s some good news:

  • microsandbox is OCI-compatible
  • You can often reuse your existing container images

On top of that, it gives you specialized sandbox types:

  • PythonSandbox
  • NodeSandbox
  • And more via their client SDKs (Python, Node.js, Rust, etc.)

So you can launch other runtimes just as easily. For example, a Node.js workload:

import asyncio
from microsandbox import NodeSandbox

# Assumes MCP_SERVER_URL is set to your remote server
async def main():
    async with NodeSandbox.create(name="my-node-app") as sb:
        exec = await sb.run("console.log('Hello from a Node.js sandbox!');")
        print(await exec.output())
asyncio.run(main())

The result:

  • Each sandbox runs inside its own microVM
  • A compromise inside one sandbox does not compromise the host
  • You get strong isolation without building everything from scratch

6. Where to Go from Here

If you want to go even deeper, there’s a next level:

  • Running raw Firecracker directly
  • Wiring up your own:
  • VM lifecycle management
  • Networking
  • Storage
  • Image management

This is powerful — but it’s also a big engineering lift.

If you’re interested in the full Firecracker-from-scratch path, keep an eye out for deep-dive guides that walk through compiling Firecracker, setting up tap devices, configuring block devices, and managing thousands of microVMs efficiently.

For most builders, though, you don’t need that on day one.

7. Final Word: Start Simple, Then Level Up

Here’s the practical takeaway:

  1. Start with containers.
  • They’re good enough for most early workloads.
  • They’re fast, cheap, and everyone knows how to use them.
  1. Harden them properly.
  • Non-root user, minimal images, tight limits, dropped capabilities, read-only filesystems.
  1. Upgrade to microVMs when your threat model changes.
  • Anonymous users, untrusted code, sensitive data, higher visibility.

It’s a spectrum:

  • On one end: Simple, container-only setups that get you shipping fast.
  • On the other: Hardware-isolated microVM platforms designed for hostile workloads.

You don’t have to start on the secure end of the spectrum. But as your scale, risk, and exposure grow, you should move in that direction.

When that happens, migrating from a container-based prototype to a microVM-backed system (using tools like microsandbox) is a logical next step—not a total rewrite.

Other Resources


메타데이터
post_id
1fa4803b7bdf
slug
self-hosted-sandboxes-how-to-pick-between-containers-and-microvms-1fa4803b7bdf
url
https://medium.com/@odafe41/self-hosted-sandboxes-how-to-pick-between-containers-and-microvms-1fa4803b7bdf
canonical_url
https://medium.com/@odafe41/self-hosted-sandboxes-how-to-pick-between-containers-and-microvms-1fa4803b7bdf
author_url
https://medium.com/@odafe41
status
ok
fetched_at
2026-06-09 15:37:30