# Dorkerfile reference
A Dorkerfile describes an Android environment. Directives are case-insensitive,
`#` starts a comment, and values may be quoted (`INSTALL "my app/app.apk"`).
`FROM` must come first. Duplicate singleton directives are rejected. Errors
include `file:line:col` and, for typos, a suggestion.
```dorkerfile
FROM android:35
MEMORY 2GB
CPUS 2
DISK 4GB
DEVICE pixel_7
RESOLUTION 1080x2400
HEADLESS true
INSTALL ./app/build/outputs/apk/debug/app-debug.apk
SETTING window_animation_scale 0
SETTING secure show_ime_with_hard_keyboard 0
SNAPSHOT ready
CMD launch com.example.application
```
| Directive | Arguments | Notes |
|---|---|---|
| `FROM` | `android:<api>[-<flavour>]` | Resolved against **installed** system images for the host ABI; `dorker pull` (no arguments) installs the one named here, and `dorker build` / `dorker run` offer to install it when it is missing (they ask on a terminal; `--pull` or `DORKER_PULL=1` installs without asking). `android:37` means the base release (`android-37.0`), `android:37.1` a specific minor release, and `-<flavour>` an explicit flavour such as `android:37-google_apis_ps16k`. For the Google Play Store image write `FROM android:35-playstore` (short for `android:35-google_apis_playstore`; `playstore_ps16k` is the 16 KB-page one). The default `google_apis` image already has Google Play services (what Android Auto needs); `-playstore` adds the Play Store app, and its system is not rootable. `dorker versions` lists every version and flavour you can use (installed and available to pull); `pull` reads what Google actually publishes (some APIs only have 16 KB-page flavours) and lists the real choices when nothing matches. Without a flavour, `google_apis` is preferred, then `default`, then others |
| `MEMORY` | `2GB`, `2048MB` | 1GB-32GB. Default 2GB |
| `CPUS` | integer | 1-16, at most the host's cores. Default 2 |
| `DISK` | `4GB` | Userdata partition size, 2GB-64GB. Default 4GB |
| `DEVICE` | profile name | Written to the AVD (`hw.device.name`). Default `pixel_7` |
| `RESOLUTION` | `WxH` | Default `1080x2400` |
| `HEADLESS` | `true`/`false` | Default `true`. `false` opens the emulator's window (uses the windowed QEMU binary and `-gpu auto`). Verified on macOS: snapshot restore, `exec` and `screenshot` all work with the window open |
| `GPU` | `swiftshader_indirect`, `angle_indirect`, `guest`, `host`, `off`, `auto` | Emulator graphics mode. Default `host` on macOS (the Mac GPU: about 14x less CPU than software rendering while the screen is in use) and `swiftshader_indirect` elsewhere (works on GPU-less hosts and CI); `auto` when `HEADLESS false`. `host` needs a real GPU and display (fails on headless Linux VMs). Part of the snapshot identity, so it changes the image id. `dorker run --gpu` exists for base `android:<api>` runs only |
| `INSTALL` | path | Repeatable. Relative to the build context; paths that escape it (`..`, symlinks) are rejected. A `.apk` is installed as is; an `.xapk` bundle (split APKs, as apps like Android Auto ship) has its APKs installed together, base first, and any OBB files copied to `/sdcard/Android/obb/<package>/`. The file's hash is part of the image identity, so replacing the file rebuilds the image |
| `FETCH` | path | Repeatable. A host script (executable, path relative to the build context) that creates `INSTALL` files that are missing, for example by downloading them. It runs **only when an `INSTALL` file does not exist**, before the build, in the Dorkerfile's folder, with its stdout sent to stderr. So a cached build downloads nothing and a file that is already there is never replaced: delete it, or run the script yourself, to refresh it. If the file is still missing afterwards the build fails (exit 2); a failing script fails it with exit 4. Like `PROVISION` it is code on your machine, so it needs the same consent: a prompt on a terminal (once for every script that will run), `--allow-provision` or `DORKER_ALLOW_PROVISION=1` otherwise, and the MCP server refuses it |
| `PROVISION` | path | Repeatable. A host script (must be executable, path relative to the build context like `INSTALL`) that runs once while the image is built, against the build emulator. It gets the adb serial as its only argument and in `ANDROID_SERIAL` / `DORKER_SERIAL`, the adb path in `ADB`, and runs in the Dorkerfile's folder; its stdout goes to stderr. Scripts run in order, after the APKs and settings are persisted and just before the snapshot, so things they start in memory stay running in the snapshot. Because the script is arbitrary code on your machine, building asks first on a terminal (`Run them now? [y/N]`, default no); without a terminal (scripts, CI, the MCP server) the build is refused unless you pass `--allow-provision` or set `DORKER_ALLOW_PROVISION=1`. A cache hit runs nothing, so it asks for nothing. A non-zero exit, or more than 10 minutes, fails the build (exit 4). The script's content hash is part of the image identity: editing it rebuilds, an unchanged script is a cache hit and is not run again (`dorker build --no-cache` forces it). Files the script reads, or downloads, are not tracked |
| `SETTING` | `[global\|secure\|system] key value` | Repeatable. Namespace defaults to `global`. Only `[A-Za-z0-9_.:-]` is accepted |
| `SNAPSHOT` | name | Save an emulator snapshot after provisioning. Instances restore it in a few seconds |
| `CMD` | `launch <package>` | Launches the package's launcher activity after the instance is ready and waits for its first frame (the system splash screen is gone), so an immediate `dorker screenshot` shows the app. Other forms are rejected |
Arbitrary host commands are not supported, by design.
## Builds and the lockfile
`dorker run` with no image runs the Dorkerfile in the current folder (or `-f`): it builds first, which is instant when nothing changed, so it always starts the current configuration.
`dorker build [-t repo:tag] [-f Dorkerfile] [context]` boots a build instance,
applies the directives in order, saves the snapshot, and publishes an immutable
image. The image id is a fingerprint of the system-image revision, emulator
version, resources, APK **contents**, settings and snapshot name, so an identical
rebuild is a cache hit (`--no-cache` forces a rebuild).
Provisioning ends with a flush, a guest reboot and a check that every `SETTING`
persisted, then the snapshot is saved. This makes a cold boot of the image
equivalent to a snapshot restore, and costs roughly 20 s per build.
`Dorkerfile.lock` is written next to the Dorkerfile and records the resolved
system image and revision, emulator version, device configuration and APK
SHA-256 hashes. dorker warns when the installed system image or emulator no
longer matches the lock. It does not download anything, so the lock pins what
was resolved rather than fetching it.
## Snapshots and compatibility
A snapshot is only restored when the hardware configuration, system-image
revision and emulator version match the image. Otherwise `dorker run` prints a
note and cold boots. Overriding `--memory` or `--cpus` therefore disables the
snapshot.