redroid-android13
This repo is the minimal Android 13 redroid bundle that matches the optimized 5570 instance.
It keeps only the parts that were actually used:
redroid/redroid:13.0.0MindTheGappslibhoudiniKitsune Mask / Magisk DeltaPixel 7 Prodevice profile- the
post-fs-datafix that keepsMagisk Installedand the settings page healthy
Included files
redroid.pyrun_redroid.pybootstrap_redroid.pyverify_offline_bundle.pytools/compat.py(host / GPU / runtime checks)tools/ssh_remote.py(Paramiko SSH: remotedocker/adbdiagnostics)offline/README.mdoffline/manifest.jsonstuff/mindthegapps.pystuff/houdini.pystuff/houdini_hack.pystuff/magisk.pyassets/redroid_extreme_post_fs_data.sh- local
offline/downloads/ - local
offline/images/
Offline archive
The repo tracks the offline bundle metadata under offline. The large zip/apk/tar payloads are intentionally kept out of Git and should be stored locally under offline/downloads/ and offline/images/.
Use offline/manifest.json for checksums and source metadata, and offline/README.md for the expected local layout.
Expected local files:
offline/downloads/mindthegapps.zipoffline/downloads/libhoudini.zipoffline/downloads/libhoudini_hack.zipoffline/downloads/magisk.apkoffline/downloads/magisk.versionoffline/images/redroid-redroid-13.0.0-latest.tar(optional if the base image is already in local Docker storage)
Build resource order (redroid.py)
- Base image: prefers an existing local
redroid/redroid:<ver>-latest, thenoffline/images/redroid-redroid-13.0.0-latest.tar(docker load), then a registry pull (skipped when offline-only). - Zips / APK: prefers files under
offline/downloads/whose MD5 matches the pinned upstream artifacts; downloads only if missing or mismatch (skipped when offline-only). - Strict no-network builds: pass
--offlineor setREDROID_OFFLINE=1so missing bundles fail fast instead of downloading.
Verify script notes
python verify_offline_bundle.py
magisk.versionis checked against exact size and sha256 frommanifest.json. A short version string without the expected newline/padding will reportsize mismatcheven whenmagisk.apkis fine for the image build.- A missing
offline/images/*.taris expected to FAIL here if you rely on a pre-pulled Docker image instead; that does not blockredroid.pywhen the base image already exists locally.
Dependencies
python -m pip install -r requirements.txt
Compatibility checks
The project probes the host before critical steps (no separate manual checklist required):
| Step | What it checks | Opt-out |
|---|---|---|
redroid.py |
Container runtime (docker/podman) responds; OS is plausible for redroid |
--no-compat-check |
run_redroid.py |
Same + Linux /dev/dri for GPU; binder_linux module hint on Linux |
--no-compat-check |
GPU mode (run_redroid.py):
--gpu-mode guest(default): software GLES — most stable on headless / server GPUs where host EGL inside the guest often breaks boot or adbd.--gpu-mode auto: staysguestunlessREDROID_USE_HOST_GPU=1(ortrue/yes/on) and/dev/drinodes exist; thenhostanddocker run --device …per DRI node.--gpu-mode host: pass-through when DRI exists; otherwise falls back toguestwith a warning.
Quick probe only:
python tools/compat.py
python tools/compat.py -c podman --gpu-mode guest
Build the 5570 image
python redroid.py -mtg -i -m
Build flags (short options):
| Flag | Long form | Meaning |
|---|---|---|
-mtg |
--install-mindthegapps |
Bake MindTheGapps (Google apps framework) into the image |
-i |
--install-houdini |
Bake libhoudini + redroid houdini hack (ARM translation on x86) |
-m |
--install-magisk |
Bake Kitsune Mask / Magisk bootless payload into the image |
Together they produce redroid/redroid:13.0.0_mindthegapps_houdini_magisk. None of these control the screen; display is set when you run the container (run_redroid.py).
Air-gapped (requires a complete offline/ tree):
REDROID_OFFLINE=1 python redroid.py -mtg -i -m
# or
python redroid.py -mtg -i -m --offline
Expected image tag:
redroid/redroid:13.0.0_mindthegapps_houdini_magisk
Launch the container
Headless servers have no screen to approve the USB debugging RSA dialog. Always pass --adb-insecure so boot props disable ADB RSA checks and apply before the Pixel user profile (androidboot.adb.secure=0, ro.adb.secure=0, ro.secure=0, ro.debuggable=1). Do not expose the ADB port on a public network.
Persist after reboot (default)
By default run_redroid.py does not pass --rm: the container stays on disk when it stops, and uses --restart unless-stopped so Docker/Podman will start it again after the daemon or host reboots (unless you ran docker stop <name>).
- Always restart even after explicit stop:
--restart always - No auto-restart but still keep the container (no
--rm):--restart no - One-off / dev only (old behavior, removed on stop): pass
--rm
On Ubuntu, ensure the engine starts on boot:
sudo systemctl enable docker --now
# or: sudo systemctl enable podman.socket --now # if you use Podman rootful similarly
python run_redroid.py \
--image redroid/redroid:13.0.0_mindthegapps_houdini_magisk \
--bridge libhoudini \
--device-profile pixel-7-pro \
--data-dir ~/data-redroid13 \
--port 5570 \
--replace \
--adb-insecure
On the same machine as Docker, one-shot status + logs + logcat after start:
python run_redroid.py ... --adb-insecure --diagnose --diagnose-settle 20
Then:
adb kill-server && adb start-server
adb connect 127.0.0.1:5570
adb devices
If you still see unauthorized after an upgrade, stop the container, remove persisted keys on the host, and retry:
rm -rf ~/data-redroid13/misc/adb
Remote diagnostics over SSH (Paramiko)
When redroid runs on another Linux machine, you can drive checks from your PC with tools/ssh_remote.py (uses Paramiko, declared in requirements.txt).
Do not put passwords in Git. Use a key file or a one-time prompt:
pip install -r requirements.txt
export REDROID_SSH_HOST=192.168.2.179
export REDROID_SSH_USER=eric
python tools/ssh_remote.py --password-prompt diagnose
Key-based:
export REDROID_SSH_HOST=... REDROID_SSH_USER=eric REDROID_SSH_KEY=$HOME/.ssh/id_ed25519
python tools/ssh_remote.py diagnose
Optional env: REDROID_CONTAINER (default redroid_13.0.0_mindthegapps_houdini_magisk), REDROID_ADB_PORT (default 5570). You can also pass --container-name / --adb-port.
Arbitrary remote command (note the -- before the shell snippet):
python tools/ssh_remote.py -H 192.168.2.179 -U eric --password-prompt exec -- bash -lc 'docker ps -a | head -20'
The shell subcommand is a minimal pseudo-shell only; for a full TTY, use normal OpenSSH ssh.
ADB failed to connect / connection refused
This means nothing is listening on the host port (not the same as unauthorized). Typical causes:
- Container exited — if you used
--rm, a crash removes the instance. By default the container is kept; usedocker ps -aanddocker logsto inspect. Ensure--restartis notnoif you want auto-start after reboot. androidboot.redroid_gpu_mode=host— Mesa/EGL or DRI inside the guest can fail on some hosts; Android never reaches adbd. Workaround: force software rendering:python run_redroid.py ... --adb-insecure --gpu-mode guest- Still booting — wait longer, or use
--wait-tcp 90onrun_redroid.pyto block until TCP opens and print logs if it does not.
Black screen or flickering
With --gpu-mode guest (the default), the display uses software GLES (SwiftShader). On many headless servers, SurfaceFlinger may still show black screen or flicker — try --soft-display and more RAM. Host GPU needs REDROID_USE_HOST_GPU=1 with --gpu-mode auto or --gpu-mode host when DRI works inside the guest.
What to try
-
GPU on the host: set
REDROID_USE_HOST_GPU=1and--gpu-mode auto, or--gpu-mode host. If ADB never connects, host EGL is likely broken — use default--gpu-mode guest. Manual devices example:docker run ... --device /dev/dri ...The exact device path varies (Intel/AMD/NVIDIA,
nvidia-docker, etc.); see redroid-doc GPU notes. -
Software GLES (
guest) — force low load on the virtual display (often reduces black screen / flicker):python run_redroid.py ... --adb-insecure --gpu-mode guest --soft-display --memory 4g--soft-displaysets 720×1280, 15 FPS, dpi 280. You can tune further with--extra-propif needed. -
First boot / SetupWizard can sit on a dark screen for a while. Run
bootstrap_redroid.pyonceadbshowsdeviceso provisioning and wizard are skipped as intended. -
Debug with logcat (on the host, after
adb connect):adb -s 127.0.0.1:5570 logcat -d | grep -iE 'SurfaceFlinger|EGL|GLES|redroid'
Apply the 5570 hardening
python bootstrap_redroid.py \
--serial 127.0.0.1:5570 \
--device-profile pixel-7-pro \
--proxy-host auto \
--install-extreme-script
This post-boot step:
- disables SetupWizard
- marks the device provisioned
- enables high accuracy location mode
- allows
com.android.shellto use mock location - applies the
Pixel 7 Pro / user / release-keys / locked / greenidentity
With --install-extreme-script, the validated script assets/redroid_extreme_post_fs_data.sh is copied to /data/adb/post-fs-data.d/20-redroid-extreme.sh so that:
vendor/bin/sustays visible to app namespaces after rebootPixel 7 Proproperties are re-applied on every boot
Verified 5570 state
The matching 5570 instance was already validated with:
ro.product.model=Pixel 7 Proro.build.type=userro.build.tags=release-keysro.boot.vbmeta.device_state=lockedro.boot.verifiedbootstate=green- app UID
su -V = 27001 - app UID
su -mm -c id = uid=0(root) Kitsunehome page shows a normalInstalledstateKitsunesettings page opens normally andSuList / MagiskHideis configurable
Current scope
This repo only reproduces the system environment for the optimized 5570 build.
It does not yet include:
- virtual camera
- camera HAL
- TikTok Live specific patches