Self-Hosting
The Quickstart covers the install in a few lines and gets you to your first sandbox. This page is the longer version — what’s actually happening on your server, how to add teammates, and the operational details I’ve picked up running bhatti on real hardware.
Requirements
Section titled “Requirements”bhatti v2 (krucible) self-hosts on two platforms:
- Linux with KVM —
/dev/kvmmust exist and be readable. The daemon runs under systemd (as root by default; it needs KVM). Any aarch64 or x86_64 box works. - macOS on Apple Silicon — HVF (Hypervisor.framework). No root, no
KVM; the shipped
bhatti-vmmis Developer-ID signed and notarized, carries the hypervisor entitlement, and runs under launchd. Intel Macs are not supported.
1 GB+ RAM; NVMe recommended for snapshot performance.
I run on two Raspberry Pi 5s with NVMe HATs (home, integration tests), a Hetzner box, and my own Mac laptop. Anything in that ballpark or stronger works.
Storage — no special filesystem needed
Section titled “Storage — no special filesystem needed”Unlike v1, v2 has no btrfs/reflink requirement. Copy-on-write lives
at the disk-image-format layer: each sandbox gets a thin qcow2
overlay over a shared read-only base image, so create-from-image is
instant and one base is shared by many sandboxes’ overlays — on
ext4, xfs, btrfs, and APFS alike. The overhead is ~0.5% on typical
agent/dev workloads. There’s nothing to pre-provision; /var/lib/bhatti
on your normal root filesystem is fine.
(btrfs/xfs still give you transparent compression on the base images if you want it, but it’s an optimization, not a prerequisite. If you have a v1 install on a btrfs loopback, you don’t need to carry it forward — v2 is a fresh install, not an in-place upgrade.)
Install
Section titled “Install”curl -fsSL bhatti.sh/install | sudo bashThe script will:
- Detect your platform (
linux/darwin,arm64/amd64). - Prompt for a rootfs tier (or pass
--tier <name>to skip the prompt; see below). - Download one self-contained runtime bundle — the
bhattiCLI + daemon, thebhatti-vmmVMM helper, thebhatti-netdgateway, thelibkrun(krucible) shared library, and the lean guest kernel — plus the rootfs tier. It’s laid down as a relocatable prefix under<data_dir>/runtime/; the binaries resolvelibkrunvia a relative rpath, so there’s no system-library pollution. - Install a service — a systemd unit at
/etc/systemd/system/bhatti.serviceon Linux, or a LaunchDaemon at/Library/LaunchDaemons/sh.bhatti.pliston macOS — and start the daemon. - Create an
adminuser, save its API key to the invoking user’s~/.bhatti/config.yaml(so the CLI on the same box works without an explicitbhatti setup). - Print the admin API key once — save it. Anyone with this key can do anything on this server.
Rootfs tiers
Section titled “Rootfs tiers”| Tier | What’s in it | Size |
|---|---|---|
minimal | Bare Ubuntu 24.04 | ~200 MB |
browser | + Chromium, Playwright, Node 22 | ~600 MB |
docker | + Docker Engine | ~550 MB |
computer | + Full desktop: XFCE, KasmVNC, Chromium | ~1.5 GB |
You pick one tier as the default for bhatti create (used when you
don’t pass --image). Other tiers are still installable later and can
be selected per sandbox with --image <tier>.
Non-interactive install
Section titled “Non-interactive install”For Ansible / packer / CI, skip the tier prompt with flags. The
bash -s -- ... syntax passes flags through curl | bash:
# Specific tier as the defaultcurl -fsSL bhatti.sh/install | sudo bash -s -- --tier browser
# Default minimal, plus install every other tiercurl -fsSL bhatti.sh/install | sudo bash -s -- --tiers all
# Default docker, plus also install browsercurl -fsSL bhatti.sh/install | sudo bash -s -- --tier docker --tiers browser--tier (singular) sets the default tier — what bhatti create uses
when no --image is passed. --tiers (plural, comma-separated list or
all) installs additional tiers alongside the default.
Verifying the install
Section titled “Verifying the install”From the same box where you ran the install, no setup needed:
bhatti create --name testbhatti exec test -- uname -abhatti destroy testIf the create succeeds, the daemon is healthy, the VMM has hypervisor access (KVM or HVF), the agent is reachable, and the CLI’s config was written correctly.
Adding teammates
Section titled “Adding teammates”The remote-CLI flow. On the server:
sudo bhatti user create --name alice --max-sandboxes 5 --max-cpus 4 --max-memory 4096# → API key: bht_... (shown once, save it)Send Alice the key over a secure channel (1Password, Signal, encrypted email — not Slack, not GitHub Issues). On her machine:
# CLI only, no sudocurl -fsSL bhatti.sh/install | bash
# Wire it up. Either interactive or via flags.bhatti setup --url https://your-server:8080 --token bht_...# or:bhatti setupNow Alice can bhatti create, bhatti exec, etc. — but only against
sandboxes she creates. Each user is isolated at the API layer (scoped
queries), and each user’s sandboxes get their own bhatti-netd gateway
so cross-user traffic never shares a network — see
Networking.
For driving bhatti from agents, CI, or provisioning scripts, use the
non-interactive form: bhatti setup --url ... --token .... The auth
test always runs and the command exits non-zero on failure, so your
provisioner picks up bad credentials immediately.
Custom domain (optional but recommended)
Section titled “Custom domain (optional but recommended)”By default bhatti publish generates URLs at
<alias>.<your-server-ip>.nip.io or similar — fine for testing, ugly
for sharing. To get URLs like my-app.yourdomain.com with TLS, see
Custom domain.
Backups
Section titled “Backups”What to back up:
<data_dir>/state.db— every sandbox/user/secret/template/volume row. The daemon writes WAL, so back upstate.db+state.db-wal+state.db-shmtogether, or runsqlite3 .backupfor a clean copy.<data_dir>/age.key— the encryption key for every secret. Lose it and every encrypted secret on the server is unrecoverable. Treat it like a TLS private key.<data_dir>/volumes/— standalone volumes (the ones created withbhatti volume create). Real ext4 images.- Per-sandbox files under
<data_dir>/sandboxes/are usually not worth backing up — sandboxes are reproducible from images and init scripts. For state you care about, take a named snapshot (bhatti snapshot create <name>), which writes a self-contained bundle under<data_dir>/snapshots/.
You do not need to back up <data_dir>/runtime/ — it’s the
relocatable binary/library/kernel bundle, re-laid on every install and
update, not user data.
For S3-compatible volume backups, see Volumes → backups.
Updating
Section titled “Updating”sudo bhatti update # refresh the whole runtime bundle + tierssudo bhatti update --tiers all # also pull additional rootfs tiersOr just re-run the install:
curl -fsSL bhatti.sh/install | sudo bashThe install script is idempotent — it skips components that haven’t
changed and updates the rest. Note that a bhatti update within v2 is
safe; crossing from a v1 (Firecracker) install is blocked (different
VMM, different on-disk layout) — install v2 fresh instead.
Uninstalling
Section titled “Uninstalling”# Remove binaries + service, keep <data_dir> so you can reinstallcurl -fsSL bhatti.sh/uninstall | sudo bash
# Remove everything, including all sandbox state, volumes, and age.keycurl -fsSL bhatti.sh/uninstall | sudo bash -s -- --purge--purge is destructive and unreversible — it deletes the encryption
key, every secret, every sandbox, every volume.
Where each thing lives
Section titled “Where each thing lives”The full layout is in the Configuration reference. Short version:
<data_dir>/state.db— SQLite, the source of truth<data_dir>/age.key— secret encryption key (back this up)<data_dir>/runtime/— the VMM/gateway/libkrun/kernel bundle (not user data)<data_dir>/images/— read-only base rootfs templates<data_dir>/sandboxes/<id>/— per-sandbox: CoW overlay, config drive, snapshot bits<data_dir>/volumes/— standalone volumes<data_dir>/snapshots/— named snapshots frombhatti snapshot create/etc/bhatti/config.yaml— daemon config (engine, runtime paths, listen, domain)
Next steps
Section titled “Next steps”- Users & Auth — API key rotation, per-user limits, deleting users
- Custom domain — TLS for
bhatti publish, ACME, wildcard DNS - Concepts — mental model for sandboxes and thermal states
- Architecture — what each process does and how state flows