How it works

Podroid is a real virtual machine, not a translation layer or a container trick. An Alpine Linux 3.23 guest running a custom Linux 7.0.5 kernel boots inside the Android app, with full process isolation and a persistent filesystem.

A real VM

When you tap "Start VM", the app launches a real hypervisor with a real aarch64 Alpine Linux guest. The guest kernel, the init system, the shell, and every process you run are all executing real ARM64 machine code inside a fully virtualized machine. There is no instruction translation of Alpine binaries, no Android kernel namespace trick, and no chroot. The guest sees a completely clean Linux environment.

Two backends

Podroid supports two hypervisor backends, selectable in Settings. The guest is identical either way - only the host-side virtualization layer differs.

The filesystem: squashfs + ext4 overlay

The Alpine root is split across two virtual block devices so that the base system stays compact and reproducible while your changes persist across reboots.

A minimal initramfs boots first. It mounts both block devices, stacks them with overlayfs (squashfs as lower, ext4 as upper), then calls switch_root to hand control to the real Alpine root. From that point on the guest sees a single unified filesystem, with writes silently redirected to the ext4 overlay.

Why switch_root, not chroot?

An earlier version used chroot to pivot into the overlay. That broke podman exec -it: when crun calls setns(MNT) to enter a container's namespace, it resets the root, and exec'd processes saw raw kernel paths like /mnt/overlay/proc instead of /proc. switch_root reorganizes the kernel mount tree itself, so namespace forks always see a clean /.

Init system: OpenRC

OpenRC is PID 1 inside the guest. Three app-specific services on the squashfs handle bring-up:

Boot stages

The app watches the VM's serial console and updates the persistent notification as each stage marker appears. You can follow along in real time during the first boot, where host-key generation adds a visible pause between "Starting SSH..." and the next stage.

Keeping the VM alive

A foreground Android service holds a partial WakeLock for the lifetime of the VM. The WakeLock keeps the CPU running so QEMU (or AVF) is not paused by Android's power scheduler while the app is in the background. The foreground service also shows the persistent notification required by Android for any long-running background work.

Phantom-process killer

On some Android builds, the OS aggressively kills child processes of apps running in the background, including the QEMU process. If your VM dies when you switch apps, see Limitations & troubleshooting.

Native binaries and page alignment

QEMU, the terminal bridge, and the Termux JNI library are compiled as ARM64 native binaries with 16 KB page alignment (-Wl,-z,max-page-size=16384). This alignment is mandatory on Android 13+ and on devices with 16 KB memory pages (some Pixel 9/10 variants). The app is arm64-v8a only; there is no 32-bit or x86 build.

Component summary

Component Role
Linux 7.0.5 kernel Custom-built arm64 guest kernel with overlayfs, cgroups v2, netfilter, FUSE, and VETH built in
OpenRC (PID 1) Service manager and init system inside the Alpine guest
squashfs (/dev/vdb) Read-only, compressed Alpine base system; shipped inside the APK, never written to
ext4 overlay (/dev/vda) Writable persistent layer; all user changes and installed packages live here
QEMU / AVF Hypervisor backend; QEMU (software TCG) works on any device, AVF (hardware) on supported Pixels
Foreground service Holds a partial WakeLock and the persistent notification to keep the VM running in the background

Podroid is free software (GPL). Docs for v1.2.1. Found something inaccurate? Open an issue.