PRoot-Distro
The Termux project’s utility for running rootless Linux containers on Android — and on
ordinary Linux hosts. GPL-3.0, Python (3.9+), 3,380★ / 423 forks / 5 open issues, created
2020-07-20, last pushed 2026-08-13 (the day it was read). Installed with pkg install proot-distro or
from PyPI; the CLI answers to both proot-distro and pd. T1 — the project’s own documentation,
read in full.
It gives you Ubuntu, Debian, Alpine, Arch, openSUSE or any other OCI image as a full userland on a phone, without root, without a kernel module, and without a Docker daemon.
How it gets there
Two pieces, and the split matters because only one of them is new.
An OCI registry client written against urllib. install speaks the OCI Distribution protocol
directly: it probes https://<registry>/v2/, follows the Bearer auth challenge for an anonymous token,
resolves a manifest list to your CPU, and pulls each layer with its SHA-256 verified before the blob
is promoted into the cache. Layers are then applied onto an empty rootfs with full whiteout semantics
(.wh.<name>, .wh..wh..opq); hard links become copies; device nodes and FIFOs are skipped. A cached
image reinstalls offline. build goes the other way — a Dockerfile to an OCI image with no daemon
anywhere — and push sends the result to Docker Hub, GHCR or anything OCI-compatible.
proot itself, which is the old part and the reason the whole thing works: a user-space
implementation of chroot, mount --bind and binfmt_misc that uses ptrace to intercept the
guest’s system calls and rewrite paths on the fly. No kernel privilege is involved anywhere. That is
the same move Wine/Proton makes for Windows binaries in a different register,
and compatibility-layer holds the comparison.
What it costs, in the project’s own words
The README’s Limitations section is the most candid document in this corpus, and it is worth reading against the privacy claims elsewhere here — those are self-reported and unverified, this is a maintainer listing what does not work:
- Performance. Every syscall goes through
ptrace, so filesystem-heavy work (compilers, package managers) is “noticeably slower than native execution.” - No real root. UID/GID remapping fakes it;
sudo, mounting real filesystems andiptablesfail. - No kernel features. FUSE, custom cgroup hierarchies, specific iptables targets: absent.
- No background services. systemd, OpenRC and socket activation are “generally not possible” — a long-running process is fine, an init system is not.
- No namespaces, so no container-in-container and no network namespaces: “proot is path translation, not kernel isolation.”
- No nesting. It refuses to run inside another
proot, sinceptracecannot trace a traced process. - Building is not BuildKit. Each
RUNexecutes under proot with no PID/network/IPC isolation, no cgroups, no seccomp; BuildKit-only directives (RUN --mount,--network,--security,COPY --link) are rejected with an explicit error rather than silently ignored. Multi-arch manifest lists are not produced. - No zstd layers, because Python’s
tarfilecannot read them — some current Docker Hub images simply fail to install.
Two defaults worth naming
The spoke’s founding finding is that self-hosting relocates a dependency while being sold as deleting one (synthesis, hometube). This tool restates it one layer down, and here the relocation is in the source rather than in the pitch.
Every container it builds gets Google’s DNS. After the layers are applied, /etc/resolv.conf is
replaced with 8.8.8.8 / 8.8.4.4. A user who installed a Linux distribution on their own phone
specifically so that nobody else runs it now resolves every hostname through Google by default. The
README states this plainly as a fixup; it is not hidden, and it is also not presented as a choice.
Credentials come from an environment variable. Private pulls and pushes need
PD_DOCKER_AUTH=user:password (or a token). Docker credential helpers and ~/.docker/config.json are
deliberately not read. Simpler, and it puts a password in the process environment.
Both are the shape this corpus keeps finding: the vendor is removed, and something else takes the slot.
Where the images come from
The 2020 tool shipped its own prebuilt rootfs tarballs. This one pulls from Docker Hub by default,
which is the substitution that makes it interesting for the spoke: the distribution channel is now a
commercial registry that rate-limits anonymous pulls, on a project whose entire premise is not needing
anyone’s permission. Local tarballs and OCI archives are accepted too, so the escape hatch exists —
install ./myimage.oci.tar — but the default path runs through a company.
Compare f-droid, where the same project family treats an independent repository as the real distribution channel for termux itself.
Reflexive note
This wiki runs on it. The hub’s host reports Linux localhost 6.17.0-PRoot-Distro … aarch64, so the
ptrace overhead described above is the overhead every build in this repository pays, and “no
background services” is why the hub has no daemons. Recorded because it is evidence available here and
nowhere else in the corpus: the spoke’s other sources are software the wiki has read about, and this one
is software the wiki is running.
Related
compatibility-layer · termux · proton-wine-andrevto · foss-application · self-hosting · f-droid · synthesis