Install
Requires Rust 1.85+ and ~/.cargo/bin on your PATH.
Verify:
# or
Debug install (faster compile, larger binary):
Run without installing:
Quickstart
Requires the Android SDK (emulator, platform-tools) and an installed system
image (dorker pull installs one). macOS Apple Silicon and Linux x86_64 with KVM are
validated. No Docker needed.
# Check your setup, see which Android versions you can use, install one
# Boot a headless emulator and use it
# Stop it, bring it back (cold boot, keeps its data), then delete it
Describe your environment in a Dorkerfile
A Dorkerfile (next to your app, like a Dockerfile) declares the Android environment: the
base image, resources, the APK to install, settings, and a snapshot to start from.
# Dorkerfile
FROM android:35
MEMORY 2GB
CPUS 2
DISK 4GB
DEVICE pixel_7
RESOLUTION 1080x2400
HEADLESS true
# Paths are relative to this file's folder.
INSTALL ./app/build/outputs/apk/debug/app-debug.apk
# Turn off animations: faster, less flaky UI tests.
SETTING window_animation_scale 0
SETTING transition_animation_scale 0
SETTING animator_duration_scale 0
# Save a snapshot so instances start in a few seconds,
# and open the app once an instance is ready.
SNAPSHOT ready
CMD launch com.example.app
Then build the image once and run it as many times as you like:
# Not sure which versions exist? List them (installed and available)
# Build the Dorkerfile in this folder (instant if nothing changed) and run it
# Or in the foreground; the instance is removed on Ctrl-C
On a Mac the emulator renders on the GPU by default (GPU host), which keeps it smooth
when you watch the screen with scrcpy: about 43 % CPU while scrolling against about 600 %
for software rendering. See docs/benchmarks.md.
dorker run with no image always starts what the Dorkerfile currently says: it builds
first, which is a cache hit when nothing changed, so edit the file and run again to get
the new environment. Use -f path/to/Dorkerfile for a file elsewhere. For more control,
the steps are separate commands, and an image can be named and reused:
If the base image isn't installed, dorker build and dorker run offer to install
it: on a terminal they ask first (it is a download of 1 GB or more, and Google's
android CLI accepts the SDK licences for you). In scripts and CI, where nothing can be
asked, they fail with a hint; pass --pull (or set DORKER_PULL=1) to install without
asking.
An identical rebuild is a cache hit (instant), and every dorker run gets its own isolated
copy, so instances never share state. To try it without your own app, use the sample:
# Build the tiny sample APK (needs the Android build-tools and a JDK)
Every directive is documented in docs/dorkerfile.md. More examples:
examples/basic, examples/instrumentation and
examples/counter-app, a real Gradle app with Espresso tests
(./dev.sh runs Gradle, dorker build, run, a screenshot and the tests). The example APKs
are not committed: build them with each example's make-apk.sh (or Gradle for the counter app).
Each instance gets its own private AVD under ~/.dorker (override with
DORKER_HOME). Your existing AVDs are never touched. dorker run --rm removes the
instance when it stops. ps, images and doctor accept --json.
Measured on an Apple M4 Pro, API 35, 2GB: cold boot about 15s, snapshot restore about 4s. Host memory is about 2.5GB after a cold boot and 3.3GB after a restore. Details and caveats: docs/benchmarks.md.
See what is using disk
It lists every image (tags, Android version, size, last used, how many instances use it),
every instance (with its own size) and a storage summary: totals, how much dorker prune
could reclaim, free space on the disk and the Android SDK system images dorker does not
manage. On macOS (APFS) an instance shares disk blocks with its image, so its size is only
what it wrote itself; elsewhere instances are full copies and are counted in full.
Free disk space
Every Dorkerfile change makes a new image of about 3-5 GB. dorker prune removes the ones
you no longer need (dorker ls shows what that would be):
By default it removes only images with no tag that no instance was made from; tagged
images, images behind existing instances and running instances are always kept (--all
adds unused tagged images, --instances adds stopped ones). It lists what it will remove
first and, outside a terminal, refuses to delete without --yes. It also clears the
leftovers of interrupted builds. A removed image is simply rebuilt the next time you need it.
Test, benchmark, automate
# Boot, restore, launch, disk and idle numbers
# Also time the plain official emulator, for comparison
dorker test prints a summary, preserves the raw instrumentation output and logcat
under the instance directory, and exits 7 if anything failed.
See examples/instrumentation for a runnable example.
Tests
# Unit, CLI and fake-SDK lifecycle/failure tests (no emulator needed)
# End-to-end: boots real emulators (about 3 minutes)
Use it from AI agents
dorker mcp is a Model Context Protocol server: an agent can start instances, install apps,
run commands, take screenshots, read logs and run tests, all with structured results.
See docs/mcp.md for the tools, other clients and security notes. The same
operations are available to scripts as --json on run, start, exec, install,
screenshot, test, ps, images, doctor, bench, versions, prune and ls.
Diagnostics
doctor, ls, ps, images, versions and prune colour their output on a terminal
(green ok, yellow warn, bold red FAIL, fixes and IDs in cyan, sizes in yellow). Colour is off when piped, NO_COLOR turns it off, CLICOLOR_FORCE=1 forces it on,
and --json is never coloured.
dorker -v <command> (or DORKER_VERBOSE=1) prints what dorker decides and runs to stderr: the
located SDK, the boot mode and why, the exact emulator command line and environment, state
transitions and timings. Each instance also keeps its raw emulator output in
~/.dorker/instances/<id>/emulator.log.
Exit codes
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | other error |
| 2 | invalid configuration or arguments (including Dorkerfile errors) |
| 3 | missing dependency (SDK, emulator, adb, system image); doctor failures |
| 4 | image build failed |
| 5 | instance failed to start or boot |
| 6 | adb operation failed |
| 7 | instrumentation tests failed |
| 8 | name, port or build conflict, or not enough free disk space |
dorker exec returns the guest command's own exit status.
See docs/architecture.md for design and limitations.
Limitations
- Platform: validated on macOS Apple Silicon (primary) and Linux x86_64 with KVM (Ubuntu 24.04, an Azure VM with nested virtualisation; unit tests and the full emulator end-to-end suite pass). Linux arm64 and bare-metal Linux are untested; Windows is unsupported. Linux is slower in our measurements (cold boot about 29s, snapshot restore about 9s; see docs/benchmarks.md).
- System images are installed on request, not automatically.
dorker pull android:35runs Google'sandroid sdk install(the replacement for the deprecatedsdkmanager; older command-line tools fall back tosdkmanager). The new CLI accepts the Android SDK licences (and the ARM translation licence for arm64 images) for you without prompting, sopullsays so first, and Google documents that it collects usage data (opt out withandroid --no-metrics);dorker run/buildprint this command when an image is missing. The download path itself was only verified for the already-installed and nonexistent-package cases, not a real multi-GB download. - Snapshots are strict. A change of emulator version, system-image revision, memory or CPUs invalidates an image's snapshot. dorker then cold boots (about 15s) and says so. Snapshots are host-specific and not portable between machines.
- Memory: a 2GB guest used about 2.5GB of host memory after a cold boot and 3.3GB after a snapshot restore on macOS (3-3.7GB on Linux). Idle CPU is dominated by Android background work and takes about a minute to settle.
- Builds take about 45s because dorker reboots the guest to persist provisioned state.
CMDonly supportslaunch <package>.dorker startalways cold boots (about 14s) because restoring the image snapshot would roll back the instance's data. Rendering uses the Mac GPU (host) on macOS and software rendering (swiftshader_indirect) elsewhere;GPU <mode>in the Dorkerfile selects another. No mode was faster for snapshot restore in our measurements (see docs/benchmarks.md). There is no automatic test-APK discovery.- Concurrency: creating instances concurrently is safe, and
stop,rmandstarton the same instance are serialised by a per-instance lock (the second command waits up to 60s, then exits 8). - Orphans: emulator processes that dorker started but lost track of (CLI killed mid-start) are reported by
dorker ps, not adopted or killed automatically. examples/basicandexamples/instrumentationuse tiny hand-built APKs;examples/counter-appis the realistic one.
Development
Common tasks via the Makefile:
Releasing (requires cargo-release and git-cliff):
The pre-release hook regenerates CHANGELOG.md with git-cliff from your conventional-commit history (grouped into Features, Bug Fixes, etc. per cliff.toml) and commits it alongside the version bump. Pushing the resulting v* tag triggers the release workflow, which builds the multi-platform binaries and publishes a GitHub Release whose notes are generated by git-cliff from the same config.
License
MIT