StrawDroid

StrawDroid

Docker app from junkerderprovinz's Repository

Overview

A straw droid is a practice dummy shaped like an Android phone: built like the real thing so somebody can strike at it without anybody getting hurt. This one runs the REAL Android emulator - the same AVD Android Studio starts - on a Selkies desktop, so an app under development can be installed, driven and broken without an APK ever touching a phone. NOT Android-in-a-container (ReDroid, Waydroid). Those run the Android userspace directly on the host kernel: no battery, no motion sensor, no power HAL. Doze therefore never fires, WorkManager runs more eagerly than it ever would on real hardware, and Android's limits on a dataSync foreground service never kick in. For an app whose job is syncing files in the background, a rig that is green because it never asks the question is worse than no rig at all. Android 16 (API 36), the NEWEST on purpose. It keeps the six-hour daily cap on dataSync foreground services that arrived in 14 AND the timeout that arrived in 15, so the newest level is the STRICTEST rather than a different one - and the Play Store wants a recent target level anyway. The google_apis image carries Play services and a real DocumentsUI, the SAF folder picker. Everything a phone has that the emulator can fake is switched on: a SIM that takes calls and SMS, a front and a back camera, every sensor the image offers (heart rate and wrist tilt among them), Bluetooth, and sound in the browser. Shizuku and PCAPdroid come installed for testing, and ROOT=true gives apps root. Android 14 is available as the latest-api34 tag. Flutter apps need vm.overcommit_memory=1 on the host: without it the emulator crashes as soon as one draws. Add sysctl -w vm.overcommit_memory=1 to /boot/config/go to keep it after a reboot. Screen: Selkies over HTTPS on 3001. No login. Deploy from a development machine: adb connect container-ip:5555 Android Studio then lists it as an ordinary device and deploys and debugs on it exactly like a phone on a cable. Force Doze, which is the point of using a real AVD: adb -s container-ip:5555 shell dumpsys deviceidle force-idle Needs /dev/kvm, passed in via Extra Parameters. Bottich is bare metal, so no nested virtualisation is involved. Without it the emulator will not start, and the container log says so in as many words. NOT set to restart on its own, on purpose. An emulator is a virtual machine and holds its memory whether anybody is testing or not - measured idle, with no app installed, about 6 GB. Start it when it is needed and stop it after.

StrawDroid

Build  Docker Pulls  Image Size  Android 16, API 36  Selkies  Unraid Template  Release  License: AGPL-3.0


A real Android emulator on a Selkies desktop, so an app can be installed, driven and broken without an APK ever touching a phone.


Install from Unraid's Community Applications   Download the docker-compose file   Download the source archive


Download the Windows start script   Download the Linux start script
Always downloads the latest build


A one-knight job: I build it, keep it running, work through the issues and add what people ask for, until nothing is missing. It is free, with no accounts, no telemetry, no ads and no paid tier. No asterisk anywhere. Nothing readable ever leaves your own walls. Forged on evenings and weekends, with heart and stubbornness.

If it has earned a place on your server or computer, toss a coin to your knight: it helps cover the costs and keeps the project alive. It also makes this knight's heart beat a little faster. Three ways below, whichever suits you.


Buy me a coffee   PayPal   Donate with crypto


Table of Contents

  1. What it looks like
  2. What it does
  3. Getting started
  4. How AI is used here
  5. Support this project

1. What it looks like

The emulated phone and a terminal on the Selkies desktop, seen in a browser
Android 16 on the Selkies desktop, in a browser. The strip beside the phone is the emulator's own toolbar, and the terminal is one right-click away.


The share folder in the Files app, the app drawer and the Android version page on the emulated device
Anything dropped into /share turns up under Download/share, so a build can be installed by tapping it. The app drawer shows it installed, and the device says what it is: Android 16.


2. What it does

A straw droid is a practice dummy shaped like an Android phone: built like the real thing so somebody can strike at it without anybody getting hurt.

  • The real emulator. It runs the same AVD Android Studio starts, headless on a Selkies desktop. It has a battery, a motion sensor and a power HAL, so Doze and Android's limits on background work behave as they do on a phone. Android in a container such as ReDroid or Waydroid has none of these, and there Doze never fires.
  • The screen in a browser. The desktop arrives over WebRTC, encoded on the box's own GPU, so scrolling and transitions look the way they will on a phone.
  • Deploys like a phone on a cable. adb reaches the device over the network, and Android Studio installs and debugs on it as on any other device.
  • Android 16, API 36, google_apis, x86_64. The newest level is also the strictest about background work. The image carries Play services and the real DocumentsUI, the folder picker an app asks for a folder with. For faults that only show on an older level, Android 14 (API 34) is published as well, under the latest-api34 tag.
  • A phone in full. The SIM takes calls and SMS, and its number is one that SMS apps accept. The back camera shows whatever sk camera puts in front of it: a QR code, a picture, a video or a light flashing Morse code. The front camera shows the emulator's test scene, every sensor the image offers is on (heart rate and wrist tilt among them), Bluetooth is on, and the phone's sound plays in the browser tab. The microphone hears a sound file played with sk mic, or your own voice once the microphone is switched on in the Selkies sidebar.
  • Tools for testing on board. Shizuku (Apache-2.0) is started again after every boot, for apps that need the shell's privileges, and PCAPdroid (GPL-3.0) shows which hosts an app talks to. TEST_TOOLS=false keeps both off the device.
  • Root when a test needs it. ROOT=true gives apps root through su, for testing an app's root mode. SELinux is permissive then, unlike on most rooted phones, so it stays off unless a test asks for it.
  • Nothing lost on stop. When the container stops, Android writes its files to disk first, so whatever a test did just before a docker stop is still there after the next start.
  • APKs from a share. A folder mounted at /share shows up on the device under Download/share, and the sk helper in the container's terminal installs from it.
  • A device that stays. /config holds the AVD with its apps, permissions and test data. On first boot it gets a wallpaper, dark mode, an empty home screen, the Lawnchair launcher (Apache-2.0) and the Arcticons line icons by Donnnno (GPL-3.0). Whatever you change afterwards stays as you set it.

3. Getting started

StrawDroid needs /dev/kvm. On bare metal it is simply there. Without it the emulator does not start, and the container log says so.

docker run -d --name StrawDroid \
  --device=/dev/kvm --shm-size=2gb --cpus="4" --memory="8g" \
  -e PUID=99 -e PGID=100 \
  -p 3001:3001 -p 5555:5555 \
  -v /path/to/config:/config \
  junkerderprovinz/strawdroid:latest

PUID and PGID should be the user that owns /path/to/config on the host. The values above are Unraid's nobody:users; on a plain Linux box $(id -u) and $(id -g) are usually right. On Unraid use templates/strawdroid.xml, which puts the container on its own IP so no port mapping is needed.

Flutter apps need one setting on the host. Their renderer makes the emulator reserve tens of terabytes of address space, of which it uses a few gigabytes. In Linux's default overcommit mode a reservation that large is refused, and the emulator crashes as soon as a Flutter app draws. sysctl -w vm.overcommit_memory=1 on the host fixes it; on Unraid, add that line to /boot/config/go to keep it after a reboot. The setting belongs to the host, so the container cannot change it, but its log warns while it is still 0.

A graphics card makes it fast. Without one the emulator draws on the CPU. That works on any box, but taps and scrolling lag behind. With EMULATOR_GPU=auto, the default, it draws on whichever card is passed in: for Intel or AMD graphics add --device=/dev/dri, for NVIDIA add --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all (on Unraid this needs the Nvidia Driver plugin). The READY block at the end of the container log names the renderer Android ended up on; SwiftShader or llvmpipe there means the CPU.

The container does not restart on its own. An emulator is a virtual machine and holds about 6 GB of memory even when idle, so start it when you need it and stop it after.

On Windows and on a desktop Linux, the two start scripts at the top check KVM and start the container. Docker is required either way. On Windows the script also switches on nested virtualisation for WSL2 and loads KVM there, on every run, because Docker Desktop rebuilds its WSL machine whenever it restarts. Right-click start-windows.ps1 and choose Run with PowerShell; if the execution policy stops it, powershell -ExecutionPolicy Bypass -File start-windows.ps1 runs it once. On Linux, chmod +x start-linux.sh && ./start-linux.sh. Both are plain text, so read them before you run them.

Once it runs:

Screen https://<address>:3001/, with a self-signed certificate to accept once. No login.
Deploy adb connect <address>:5555, then Android Studio treats it as an ordinary device.
Force Doze adb -s <address>:5555 shell dumpsys deviceidle force-idle
Undo it adb -s <address>:5555 shell dumpsys deviceidle unforce

The container log ends on a banner with these lines and the real address filled in.

Mount a folder read-only at /share to install builds from it. On the device it is copied to Download/share every 30 seconds (SHARE_MIRROR_SECONDS), up to 2048 MB (SHARE_MIRROR_MAX_MB). In the container's terminal, sk does the rest:

sk list                    the APKs on the share, newest first
sk install <name>.apk      install one, keeping the earlier build's data
sk push <file|dir>         copy test files into the device's Downloads
sk doze on | off           force deep idle, or release it
sk sms <number> <text>     an incoming SMS from that number
sk call <number>           an incoming call from that number
sk geo <lat> <lon> [alt]   put the device at that position, altitude in metres
sk route test-track        drive the GPS along a GPX track at its own pace,
                           or with --kmh, --speedup and --loop
sk pin 1357 | off          set or remove the screen lock PIN
sk finger enroll | touch   add a fingerprint, or touch the sensor with it
sk sensor pressure 950     set a sensor, or list them without arguments
sk seed                    test contacts, photos with GPS, music with BPM tags,
                           a video, documents, a score, a song, a GPS track, a
                           calendar event and barcode pictures on the device
sk camera qr-otp           what the back camera sees: a QR code or a picture
sk camera video <file>     a video instead, mp4 or webm, played in a loop
sk camera blink SOS [wpm]  a light flashing the text in Morse code
sk mic [--loop] <file>     what the microphone hears: a WAV, MP3, OGG or FLAC
                           file, or a click track such as click-120-bpm
sk mic off                 silence again
sk home pixel | lawnchair  the home screen app; widgets show only on pixel
sk expose <port>           reach a port the device listens on from the network
sk lang de-AT              switch the system language
sk dark on | off           dark or light theme
sk snap save <name>        save the whole device, and load, rm or list snapshots
sk reset                   back to the snapshot called "clean" in seconds
sk netlog start <package>  record which hosts an app talks to, through PCAPdroid
sk netlog stop             stop and list them
sk shot | sk record [s]    a screenshot or a screen recording
sk shell [...]             a shell on the device

Screenshots, recordings and network captures land in /config/captures. A snapshot lives with the AVD in /config and takes about 2.5 GB.

The device profile and the emulated RAM are read on the first boot only. To change them later, remove the AVD from /config/.android/avd.

Cameras, sound, the phone number and the test tools take effect on every start: EMULATOR_CAMERA_BACK (video, picture, emulated or none) and EMULATOR_CAMERA_FRONT (emulated or none), EMULATOR_AUDIO, TEST_TOOLS and ROOT (true or false), and EMULATOR_PHONE_NUMBER. The Unraid template explains each one.


4. How AI is used here

One knight builds this, and AI is one of the tools I work with, the same way I work with an editor or a compiler. It helps me write code and documentation and it checks my work, and that saves me a good many evenings. It does not make the decisions, though. I read and understand everything before it ships, and if something here breaks, that is on me and not on the tool.

You do not have to take my word for it. The code is open and every release note is written by hand. The issue tracker shows how problems actually get handled, including the ones I got wrong the first time. If you find something that is not right, open an issue and I will look at it.


5. Support this project

Problems, wishes or suggestions? You're welcome to open an issue.

A one-knight job: I build it, keep it running, work through the issues and add what people ask for, until nothing is missing. It is free, with no accounts, no telemetry, no ads and no paid tier. No asterisk anywhere. Nothing readable ever leaves your own walls. Forged on evenings and weekends, with heart and stubbornness.

If it has earned a place on your server or computer, toss a coin to your knight: it helps cover the costs and keeps the project alive. It also makes this knight's heart beat a little faster. Three ways below, whichever suits you.

Buy me a coffee   PayPal   Donate with crypto

Requirements

A CPU with hardware virtualisation, and /dev/kvm present on the host.

Download Statistics

505
Total Downloads

Related apps

Explore more like this

Explore all

Details

Repository
ghcr.io/junkerderprovinz/strawdroid:latest
Last Updated2026-10-28
First Seen2026-09-17

Runtime arguments

Web UI
https://[IP]:[PORT:3001]/
Network
br0.20
Shell
bash
Privileged
false
Extra Params
--restart=no --device=/dev/kvm --shm-size=2gb --cpus="4" --memory="8g"

Template configuration

WebUI Port (HTTPS / Selkies)Porttcp

The desktop the emulator draws on, over WebRTC. Self-signed certificate - accept it once. HTTPS is the default because browsers only allow seamless clipboard over HTTPS.

Target
3001
Default
3001
Value
3001
WebUI Port (HTTP / Selkies)Porttcp

Optional fallback. The WebUI defaults to HTTPS on 3001.

Target
3000
Default
3000
Value
3000
ADB PortPorttcp

Reached from a development machine with: adb connect container-ip:5555 . The emulator's own adb binds to loopback, so a forwarder inside the container hands this port to it; without that the port would appear open and never complete a handshake.

Target
5555
Default
5555
Value
5555
AppData (/config)Pathrw

The AVD itself, plus installed apps, granted permissions and anything a test wrote. The ONLY persistent mount by design. Without it every restart is a factory reset, which also throws away the folder permission an app was granted through the SAF picker - one of the things being tested.

Target
/config
Default
/mnt/user/appdata/strawdroid
Value
/mnt/user/appdata/strawdroid
Share for APKs and test files (/share)Pathro

Read-only. Drop an APK into this share from anywhere on the network and it appears ON THE DEVICE under Download/share, where the stock Files app and the SAF picker both find it - so it can be installed by tapping it on the phone, without a terminal. Mirrored rather than mounted, because an emulated phone is a virtual machine and a host directory is not reachable from inside it by any mount; the mirror compares sizes and timestamps and only sends what differs. From the container's terminal instead: sk list to see them, sk install name.apk to put one on the device, sk push file for test files. Read-only on purpose: this rig runs unfinished code, which has no business writing to a share full of everything else.

Target
/share
Default
/mnt/user/download
Value
/mnt/user/download
Emulator rendering (EMULATOR_GPU)Variable

How the emulator draws. auto uses a graphics card when one is passed in and the CPU otherwise, which works everywhere but is slow. Intel or AMD: add --device=/dev/dri to Extra Parameters. NVIDIA: add --runtime=nvidia to Extra Parameters and set NVIDIA_VISIBLE_DEVICES below; this needs the Nvidia Driver plugin. host forces the GPU, swiftshader_indirect forces the CPU. The READY block at the end of the log names the renderer Android ended up on.

Target
EMULATOR_GPU
Default
auto
Value
auto
NVIDIA GPU (NVIDIA_VISIBLE_DEVICES)Variable

Only with an NVIDIA card and --runtime=nvidia in Extra Parameters: all, or the card's UUID from the Nvidia Driver plugin page. Empty leaves the card out.

Target
NVIDIA_VISIBLE_DEVICES
Device profile (EMULATOR_DEVICE)Variable

Which device the emulator pretends to be. Decides screen size and density, which is what a layout is actually judged against. Read on FIRST boot only: the AVD is created once and then kept, so changing this later needs the AVD removed from /config/.android/avd.

Target
EMULATOR_DEVICE
Default
pixel_6
Value
pixel_6
Emulated RAM in MB (EMULATOR_RAM)Variable

Memory given to the emulated phone. Read on FIRST boot only, same as the device profile. Below about 1536 Android 14 boots badly or not at all.

Target
EMULATOR_RAM
Default
2048
Value
2048
Phone number (EMULATOR_PHONE_NUMBER)Variable

The SIM's number, digits only. The emulator's own default, 555-521-5554, is not a valid number to libphonenumber, so SMS apps that check numbers hide it. The default here is libphonenumber's US example number. Incoming SMS and calls come from the container's terminal: sk sms number text, sk call number.

Target
EMULATOR_PHONE_NUMBER
Default
12015550123
Value
12015550123
Back camera (EMULATOR_CAMERA_BACK)Variable

picture shows a QR code that sk camera swaps for another code or a picture of your own, so scanning apps can be tested. emulated shows the emulator's moving test scene. none removes the camera, for testing how an app copes without one. imagefile:path and videofile:path show a picture or a video from inside the container.

Target
EMULATOR_CAMERA_BACK
Default
picture
Value
picture
Front camera (EMULATOR_CAMERA_FRONT)Variable

Same choices as the back camera.

Target
EMULATOR_CAMERA_FRONT
Default
emulated
Value
emulated
Sound (EMULATOR_AUDIO)Variable

true sends the phone's sound to the browser tab, through the desktop's own audio. false starts the emulator without audio.

Target
EMULATOR_AUDIO
Default
true
Value
true
Test tools (TEST_TOOLS)Variable

true installs Shizuku and PCAPdroid once on each new device and starts the Shizuku server again after every boot, so apps that need it find it running. PCAPdroid shows which hosts an app talks to, without root. false leaves both alone.

Target
TEST_TOOLS
Default
true
Value
true
Root for apps (ROOT)Variable

true gives apps root through su, for testing an app's root mode. The first start with it reboots the phone once to make the system partition writable. SELinux runs permissive then, unlike on most rooted phones, so leave it false unless a test needs root.

Target
ROOT
Default
false
Value
false
Extra emulator arguments (EMULATOR_EXTRA_ARGS)Variable

Passed to the emulator verbatim, for the occasional one-off (-writable-system, -dns-server, -netdelay). Empty is the ordinary case.

Target
EMULATOR_EXTRA_ARGS
AVD name (AVD_NAME)Variable

The name of the virtual device inside /config. Changing it creates a SECOND device rather than renaming the first, which is a way to keep two side by side.

Target
AVD_NAME
Default
strawdroid
Value
strawdroid
WebUI UsernameVariable

Optional. Leave EMPTY together with the password for no login. Set both to enable login.

Target
CUSTOM_USER
WebUI PasswordVariable

Optional. Leave EMPTY together with the username for no login.

Target
PASSWORD
Keyboard Layout (KEYBOARD_LAYOUT)Variable

X11 keyboard layout for the streamed desktop (us, de, ...). Typing into the emulator is half of what this rig is for.

Target
KEYBOARD_LAYOUT
Default
us
Value
us
Timezone (TZ)Variable

Container timezone. It reaches the emulated Android too, which matters for anything scheduled by the clock.

Target
TZ
Default
Europe/Vienna
Value
Europe/Vienna
User ID (PUID)Variable

User the desktop and the emulator run as. Default 99 (nobody on Unraid).

Target
PUID
Default
99
Value
99
Group ID (PGID)Variable

Group. Default 100 (users on Unraid).

Target
PGID
Default
100
Value
100