The best Android research setup to beat root. (Get it? Beat... root? It sounded funnier in my head)
Beetroot aims to simplify the process of raising lightweight rooted Android environments — worry less about how it's done, so you can focus on what you want to achieve.
Configure your environment quicker, with support for Magisk modules, LiteGapps, and Frida. Want to spin up a new phone with the same config? Everything is already in your config, just run beetroot create & beetroot up to raise it.
$ beetroot create alpha
$ beetroot up alpha
[beetroot] alpha up — ADB localhost:5555, Frida localhost:27042
$ beetroot ls
NAME KIND IDX ADB FRIDA STATUS PATH
alpha redroid 0 localhost:5555 localhost:27042 running /home/you/alpha
uv tool install git+https://github.com/Xiddoc/Beetroot.git
beetroot build # one-time: build the redroid base image
beetroot create alpha # creates ./alpha/ with beetroot.yaml
beetroot create beta --path ~/work/beta # or wherever you want it
beetroot register ~/already-built-instance # adopt an existing dir
beetroot up alpha
beetroot shell alphaGApps intent for beetroot build: none, minimal (default), full. Pin a specific distribution with --gapps-vendor litegapps|opengapps|mindthegapps.
Hacking on Beetroot itself? uv tool install . from a checkout, or uv sync + uv run beetroot <verb>.
The host-side frida CLI is exposed via a [frida] extra. Install with uv tool install 'beetroot[frida]' so a frida binary is on your PATH — point it at an instance with frida -H "$(beetroot frida-addr <name>)"; plain installs omit it (beetroot frida-addr still works, it only prints a port).
- Android 14 by default (redroid base, headless, low-FPS) — pick 11, 12, 13, or 14 via
android.versioninbeetroot.yaml - Magisk root with Zygisk + denylist; GMS auto-denylisted
- LiteGapps + Houdini ARM-on-x86_64 translation
- Frida server (opt-in, version-pinned per instance) — declare a
frida:block inbeetroot.yaml(or copyexamples/with-frida.yamlover the freshly-created file) and the CLI downloads, caches, and bind-mounts the matchingfrida-serverinto the container - Drop-in Magisk module flashing via
beetroot.yaml beetrootCLI — lifecycle (create/register/up/down/destroy), shell + module management, health checks (status/doctor), and abuildbootstrap. See the CLI reference for every verb.- Already have a rooted phone?
beetroot adopt <adb-serial>registers it under the same registry — the samebeetroot shell/beetroot frida-addr/beetroot moduleverbs dispatch via the hostadbCLI instead of compose. No on-disk container; the device is managed outside Beetroot. - Runs on binderless hosts. redroid needs the kernel
binderdriver; thebinder: auto|host|vmswitch picks how that's satisfied.auto/hostuse the host kernel's binder (loading the module if needed), whilevmboots redroid inside a QEMU micro-VM that ships its own binder-enabled kernel — for hardened/nomodulesandboxes where the host can't provide it. Seeexamples/vm.yaml. - Pluggable backends. Beyond the in-tree redroid, adb, and vm backends, Beetroot ships a small extension surface so a third-party package can ship a custom backend (cloud-emulator service, network-adb gateway, …) in ~30 LOC + one
[project.entry-points."beetroot.backends"]line. See Adding a backend for the recipe.
Full documentation lives at https://iliketo.party/Beetroot/.
| Page | What's there |
|---|---|
| Installation | Prerequisites, install paths, the [frida] extra |
| CLI reference | Every verb, every flag |
| Configuration | beetroot.yaml schema, starter examples, resource defaults |
| Port allocation | Stride-of-10 mapping, overrides |
| Architecture | Image build, orchestration, boot flow |
| Filesystem layout | Per-instance state, what's gitignored |
| Python API | from beetroot import Instance, Manager — drive Beetroot programmatically |
| Adding a backend | Ship a third-party device backend in ~30 LOC |
| Migration | Per-release schema-migration walkthroughs (v0.2→v0.3, v0.3→v0.4, v0.4→v0.6) |
| Troubleshooting | Common breakages and how to unstick them |
Contributors should read CLAUDE.md for the development workflow (uv, ruff, mypy, pytest, 100% coverage gate).