{"id":13897662,"url":"https://github.com/termux/proot-distro","last_synced_at":"2026-05-30T02:01:10.522Z","repository":{"id":37976421,"uuid":"281222245","full_name":"termux/proot-distro","owner":"termux","description":"An utility for managing installations of the Linux distributions in Termux.","archived":false,"fork":false,"pushed_at":"2026-05-29T23:59:22.000Z","size":2950,"stargazers_count":3103,"open_issues_count":4,"forks_count":393,"subscribers_count":56,"default_branch":"master","last_synced_at":"2026-05-30T00:14:16.682Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/termux.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-07-20T20:46:15.000Z","updated_at":"2026-05-29T23:59:17.000Z","dependencies_parsed_at":"2023-11-09T11:25:38.488Z","dependency_job_id":"cb380b1c-ad08-42e7-bc47-b378fed3f2ba","html_url":"https://github.com/termux/proot-distro","commit_stats":{"total_commits":672,"total_committers":31,"mean_commits":"21.677419354838708","dds":0.5238095238095238,"last_synced_commit":"2abfb39d2e3e17e00a9a57187a036cd10a1e3742"},"previous_names":[],"tags_count":156,"template":false,"template_full_name":null,"purl":"pkg:github/termux/proot-distro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/termux%2Fproot-distro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/termux%2Fproot-distro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/termux%2Fproot-distro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/termux%2Fproot-distro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/termux","download_url":"https://codeload.github.com/termux/proot-distro/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/termux%2Fproot-distro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33677261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T18:03:47.449Z","updated_at":"2026-05-30T02:01:10.508Z","avatar_url":"https://github.com/termux.png","language":"Python","funding_links":[],"categories":["Shell","Python"],"sub_categories":[],"readme":"# PRoot-Distro\n\nPRoot-Distro is a utility for managing rootless Linux containers in\n[Termux](https://termux.dev) and on regular Linux hosts. It uses\n[proot](https://proot-me.github.io/) to provide a chroot-like\nenvironment without requiring root access on the device.\n\nContainers are created by pulling Docker/OCI images directly from\nDocker Hub or any compatible registry — or by extracting a local\ntarball / OCI image archive. The container filesystem is assembled from\nthe image layers and stored locally, ready to be entered at any time.\n\nPRoot-Distro can also **build** OCI images from a Dockerfile (no Docker\ndaemon required), storing the result in the local manifest cache or\nexporting it as a standalone OCI tarball.\n\n---\n\n## Table of contents\n\n1. [Introduction](#introduction)\n2. [Installation](#installation)\n3. [Quick start](#quick-start)\n4. [Commands](#commands-reference)\n   * [`install`](#install--install-a-container)\n   * [`build`](#build--build-an-image-from-a-dockerfile)\n   * [`push`](#push--push-a-built-image-to-a-registry)\n   * [`login`](#login--start-a-shell-inside-a-container)\n   * [`run`](#run--run-the-image-defined-entrypoint)\n   * [`list`](#list--list-installed-containers)\n   * [`remove`](#remove--delete-a-container)\n   * [`rename`](#rename--rename-a-container)\n   * [`reset`](#reset--reinstall-a-container-from-scratch)\n   * [`backup`](#backup--archive-a-container)\n   * [`restore`](#restore--restore-a-container-from-a-backup)\n   * [`copy`](#copy--copy-files-to-or-from-a-container)\n   * [`sync`](#sync--synchronize-files-to-or-from-a-container)\n   * [`clear-cache`](#clear-cache--delete-the-download-cache)\n5. [How PRoot-Distro works](#how-proot-distro-works)\n6. [Storage layout](#storage-layout)\n7. [Environment variables](#environment-variables)\n8. [Shell completions](#shell-completions)\n9. [Limitations](#limitations)\n10. [Donate](#donate)\n\n---\n\n## Introduction\n\nPRoot-Distro lets you run a full Linux userland — Ubuntu, Debian,\nAlpine, Arch, openSUSE, distroless server images, anything available as\na Docker/OCI image — on top of Termux on an Android device, or on top\nof a regular Linux distribution, **without** root, **without** a kernel\nmodule, and **without** a Docker daemon.\n\nTypical use cases:\n\n- Running a desktop-class Linux distribution on a phone or tablet.\n- Cross-compiling for a different CPU architecture using QEMU user-mode.\n- Spinning up server software (Nginx, Nextcloud, PostgreSQL, etc.) on\n  Android by reusing the same OCI images you'd run on a server.\n- Building custom OCI images from a Dockerfile, on-device, without\n  needing a Docker daemon — and pushing them straight to Docker Hub,\n  GitHub Container Registry, or any other OCI-compatible registry.\n- Trying a distribution non-destructively: install, mess around,\n  `proot-distro remove` when done.\n\nThe CLI is exposed both as `proot-distro` and the shorter alias `pd`.\n\n![Screenshot image](https://raw.githubusercontent.com/termux/proot-distro/master/screenshot.png)\n\n### Installation\n\nPRoot-Distro is primarily distributed via Termux's `pkg` package\nmanager and via PyPI. Python 3.9 or newer is required. The only runtime\ndependency is `proot` (and, optionally, a `qemu-user-*` package for\ncross-architecture containers).\n\n#### On Termux (Android)\n\nInstall Termux from\n[F-Droid](https://f-droid.org/en/packages/com.termux/) or the\n[Termux GitHub Releases](https://github.com/termux/termux-app/releases).\nThen, inside Termux:\n\n```sh\npkg install proot-distro\n```\n\nThis pulls in `proot` automatically as a dependency.\n\nTo install the latest published version from PyPI instead:\n\n```sh\npkg install python proot\npip install proot-distro\n```\n\n#### On a regular Linux host\n\n```sh\n# Install proot via your distro's package manager, e.g. on Debian/Ubuntu:\nsudo apt install proot python3-pip\n\npip install proot-distro          # from PyPI\n# or\ngit clone https://github.com/termux/proot-distro\ncd proot-distro\npip install .                     # from a local checkout\npip install -e .                  # editable install for development\n```\n\n### First-run check\n\nOn startup the tool verifies that `proot` is available. If it isn't:\n\n- On **Termux**, with an interactive terminal, you are prompted to\n  install it via `pkg install -y -q proot`.\n- Otherwise, an install hint is printed and the program exits.\n\nPRoot-Distro also refuses to run inside another `proot` (nested proot\nis not supported by `proot` itself) and prints a warning if launched\nas the `root` user.\n\n### Quick start\n\n```sh\n# Install Ubuntu 24.04 from Docker Hub\nproot-distro install ubuntu:24.04\n\n# Start a shell inside the container\nproot-distro login ubuntu\n\n# Same thing, but using the short command alias\npd sh ubuntu\n\n# Run a single command and exit\nproot-distro login ubuntu -- /bin/uname -a\n\n# List all installed containers\nproot-distro list\n\n# Build and install a custom image from a Dockerfile\nproot-distro build -t myapp:1.0 --install-as myapp ./mycontext\n\n# Publish the built image to a registry\nexport PD_DOCKER_AUTH=myuser:mypassword\nproot-distro push myuser/myapp:1.0\n\n# Rebuild from scratch (loses all in-container data)\nproot-distro reset ubuntu\n\n# Permanently remove a container\nproot-distro remove ubuntu\n```\n\n---\n\n## Commands reference\n\nThe `pd` short alias works everywhere `proot-distro` does.\n\nEvery command supports `--help` (also `-h`, `--usage`), which prints\nhelp text laid out for the current terminal width.\n\n### `install` — Install a container\n\n```\nproot-distro install [OPTIONS] (IMAGE or PATH)\nAliases: add, i, in, ins\n```\n\nPull a Docker/OCI image and create a container from it, or extract one\nfrom a local archive file.\n\n**Options:**\n\n| Option | Description |\n|---|---|\n| `-n`, `--name NAME` | Set a custom local name for the container. Defaults to the image name without tag/registry, or the archive filename without extension. Must start with a letter or digit; may contain only letters, digits, `_`, `.`, `-`. Empty name is rejected. The deprecated long form `--override-alias` is still accepted. |\n| `-a`, `--architecture ARCH` | Override the target CPU architecture. Accepts native names (`aarch64`, `arm`, `i686`, `riscv64`, `x86_64`) or Docker platform strings (`linux/arm64`, `linux/amd64`, `linux/arm/v7`, `linux/386`, `linux/riscv64`). Defaults to the host CPU. |\n| `-q`, `--quiet` | Suppress non-error output. |\n\n#### From a Docker/OCI registry\n\n`IMAGE` is a standard Docker image reference:\n\n| Form | Example |\n|---|---|\n| Official image | `ubuntu:24.04` |\n| Official, no tag (uses `latest`) | `alpine` |\n| User image | `myuser/myimage:tag` |\n| Custom registry | `ghcr.io/foo/bar:latest` |\n\nCustom registries are detected by the first path component containing\n`.` or `:` (i.e. a hostname). Public images on `ghcr.io`,\n`quay.io`, `registry.gitlab.com`, etc. are pulled with an anonymous\nBearer token discovered from each registry's `/v2/` challenge.\n\n**Private images** require credentials. Set `PD_DOCKER_AUTH` to\n`username:password` (or `username:PAT`) before running the install\ncommand. The colon separator is mandatory — the value is sent as HTTP\nBasic auth to the registry's token endpoint to obtain a scoped bearer\ntoken:\n\n```sh\n# Docker Hub private image\nexport PD_DOCKER_AUTH=myuser:mypassword\nproot-distro install myuser/private-image:tag\n\n# GitHub Container Registry — use your GitHub username and a PAT\n# with the read:packages scope\nexport PD_DOCKER_AUTH=myuser:ghp_xxx\nproot-distro install ghcr.io/myorg/private-image:tag\n\n# Any other OCI registry\nexport PD_DOCKER_AUTH=myuser:mypassword\nproot-distro install registry.example.com/private/image:tag\n```\n\nWhen the env var is set, the authentication progress line notes\n`(user credentials)` so you can confirm your credentials are being\npicked up.\n\nLayers are cached in `$BASE_CACHE_DIR/oci_layers/` and reused on\nsubsequent installs. If both the resolved manifest (cached in\n`$BASE_CACHE_DIR/oci_manifests/`) and all of its layers are already\npresent, installation runs fully offline.\n\n**Examples:**\n\n```sh\nproot-distro install ubuntu:24.04\nproot-distro install alpine:3.21 --name my-alpine\nproot-distro install debian:bookworm --architecture aarch64\nproot-distro install ghcr.io/myorg/myimage:latest\nproot-distro install nextcloud:32\n```\n\n#### From a local archive\n\n`IMAGE` can also be a path to a local archive file. A path is\nrecognised only when it starts with `/`, `./`, `../`, or `~` — a bare\ntoken like `ubuntu` is always treated as a Docker image, even if a file\nby that name happens to exist in the current directory.\n\nTwo archive formats are supported, auto-detected by reading the first\n500 entries of the archive:\n\n- **Plain rootfs tarball** — a tar archive whose top-level entries form\n  a standard Linux filesystem (`bin/`, `etc/`, `usr/`, …). The tool\n  scores strip levels 0–4 and picks the one that lands the most\n  recognised rootfs directories at the root. Supported compression:\n  gzip, bzip2, xz, lzma, or uncompressed. No `manifest.json` is written\n  for this format (so `reset` and `run` are not available).\n- **OCI image layout** — a tar archive that contains an `oci-layout`\n  file at its root (as produced by `docker save`, `skopeo copy\n  oci-archive:`, or similar tools). Layers are applied in order with\n  full whiteout semantics, layer blobs are cached, and `manifest.json`\n  is written so `reset` and `run` work like with a registry-pulled\n  image.\n\nThe container name is derived from the archive filename by stripping\nknown extensions (`.tar`, `.tar.gz`, `.tgz`, `.tar.bz2`, `.tbz2`,\n`.tar.xz`, `.txz`, `.tar.lzma`, `.tlzma`, `.oci.tar`, `.oci.tar.gz`,\n`.oci.tar.xz`) and sanitising the result. Use `--name` to set an\nexplicit name.\n\n**Examples:**\n\n```sh\n# Plain rootfs tarball\nproot-distro install ./alpine-rootfs.tar.gz\n\n# OCI image layout saved with docker\ndocker save myimage:latest -o myimage.oci.tar\nproot-distro install ./myimage.oci.tar --name myimage\n\n# Explicit name and architecture override\nproot-distro install /tmp/debian-arm.tar.xz --name debian --architecture arm\n```\n\nAfter installation the resolved image tag is shown (e.g.\n`Installing 'ubuntu:24.04'`). If the image defines an `Entrypoint`, a\n`Run entrypoint: proot-distro run \u003cname\u003e` hint is printed alongside\n`Start shell: proot-distro login \u003cname\u003e`.\n\n#### From an URL\n\nWhen URL is specified instead of local path, the content will be fully\ndownloaded and then processed by same method used for local file installation.\n\nOnly HTTP or HTTPS links are supported.\n\nThe default container name derived from the last URL component. Use `--name`\nto override.\n\n---\n\n### `build` — Build an image from a Dockerfile\n\n```\nproot-distro build [OPTIONS] [PATH]\n```\n\nBuild an OCI/Docker-compatible image from a Dockerfile. `PATH` is the\nbuild context directory (default `.`); all `COPY`/`ADD` source paths\nare resolved relative to it.\n\nBy default the built image is stored in the local manifest cache\nunder the tag given by `--tag` (defaulting to\n`\u003cbasename(PATH)\u003e:latest`). A subsequent\n`proot-distro install \u003ctag\u003e` finds the manifest in the cache and\ninstalls entirely without network access.\n\n**Options:**\n\n| Option | Description |\n|---|---|\n| `-f`, `--file PATH` | Use a Dockerfile at PATH instead of `\u003cPATH\u003e/Dockerfile`. Pass `-` to read the Dockerfile from stdin. |\n| `-t`, `--tag REF` | Image reference to assign. Repeatable. Defaults to `\u003cbasename(PATH)\u003e:latest`. |\n| `--build-arg K=V` | Set a build-time `ARG`. Only `ARG`s declared in the Dockerfile are honoured. Repeatable. |\n| `--architecture ARCH` | Target CPU architecture (default: host). Accepts proot-distro names (`aarch64`, `arm`, `i686`, `riscv64`, `x86_64`) or Docker platform strings (`linux/arm64`, …). |\n| `--target STAGE` | Stop after the named stage of a multi-stage build. |\n| `--emulator PATH` | Override the QEMU user-mode binary for cross-architecture builds. |\n| `-o`, `--output FILE` | Write the built image as an OCI image-layout tarball to FILE. Compression is inferred from the extension (`.oci.tar`, `.oci.tar.gz`, `.oci.tar.xz`). Repeatable. |\n| `--install-as NAME` | After build, install the image as a container named NAME. |\n| `--no-cache` | Disable per-step build caching. |\n| `-v`, `--verbose` | Echo each instruction and stream `RUN` output. |\n| `-q`, `--quiet` | Suppress non-error output. |\n\n**Supported Dockerfile instructions:**\n\n`FROM` (including multi-stage with `AS name`, `FROM scratch`,\n`FROM \u003cstage\u003e`, `COPY --from=\u003cstage-or-image\u003e`), `RUN` (shell + JSON\nexec + here-doc forms), `COPY` (with `--from`, `--chown`, `--chmod`),\n`ADD` (local files, automatic tar extraction, remote URL fetch),\n`CMD`, `ENTRYPOINT`, `ENV`, `ARG` (with `--build-arg`), `LABEL`,\n`MAINTAINER`, `USER`, `WORKDIR`, `EXPOSE`, `VOLUME`, `STOPSIGNAL`,\n`HEALTHCHECK`, `SHELL`, `ONBUILD`.\n\nParser directives recognised at the top of the file: `# syntax=…`\n(recorded; not acted on), `# escape=` (`\\` or `` ` ``; changes the\nline-continuation character), `# check=…` (recorded; not acted on).\n\nBuildKit-only features (`RUN --mount`, `RUN --network`,\n`RUN --security`, `COPY --link`, `COPY --parents`) are rejected with\nan explicit error.\n\n**`proot` requirement:**\n\nIf the Dockerfile contains any `RUN` (or `ONBUILD RUN`) instruction,\n`proot` must be installed because each `RUN` is executed against the\nin-progress rootfs under `proot`. Dockerfiles composed only of\nmetadata + `COPY`/`ADD`/`ENV`/etc. instructions build in pure-Python\nmode and do not require `proot` — they are useful, for example, for\nassembling distroless images from prebuilt artefacts on a host that\ndoesn't have `proot` available.\n\n**Output variants:**\n\n| Output | Trigger | Format |\n|---|---|---|\n| Manifest cache | Always (free) | `$BASE_CACHE_DIR/oci_manifests/\u003ckey\u003e.json` referencing layer blobs in `$BASE_CACHE_DIR/oci_layers/`. Installable via `proot-distro install \u003ctag\u003e` with no network access. |\n| OCI tarball | `-o`/`--output FILE` | Standard OCI image-layout tarball (`oci-layout`, `index.json`, `blobs/sha256/*`). Installable via `proot-distro install ./FILE`; also consumable by `docker load`. |\n| Container | `--install-as NAME` | Installed container at `containers/\u003cNAME\u003e/`. Performed after the build by invoking the install command with the just-built tag. |\n\n**Build cache:**\n\nEach instruction is keyed by a recipe hash combining the parent layer\ndigest, the instruction text (with flags and here-doc bodies), and\nthe relevant inputs (file digests for `COPY`/`ADD`, env+ARG state for\n`RUN`). A cache hit applies the previously-built layer instead of\nre-running the instruction. Pass `--no-cache` to skip cache lookups.\n\nThe build cache index lives at\n`$BASE_CACHE_DIR/build_cache_index.json`; layer blobs themselves are\nstored alongside registry-pulled blobs in `$BASE_CACHE_DIR/oci_layers/`.\n`proot-distro clear-cache` deletes both.\n\n**Examples:**\n\n```sh\n# Build with the default tag (basename of '.' + ':latest')\nproot-distro build .\n\n# Build with an explicit tag and install in one step\nproot-distro build -t myapp:1.0 --install-as myapp .\n\n# Cross-arch build, write to an OCI tarball\nproot-distro build -t myapp:arm64 \\\n    --architecture aarch64 \\\n    --output myapp-arm64.oci.tar.gz .\n\n# Build a specific stage of a multi-stage Dockerfile\nproot-distro build -t myapp:debug --target debugger .\n\n# Read the Dockerfile from stdin (build context is still required)\ncat Dockerfile.test | proot-distro build -f - -t myapp:test .\n\n# Build-arg propagation into RUN\nproot-distro build --build-arg HTTP_PROXY=$HTTP_PROXY -t myapp .\n```\n\n**Limitations:**\n\n`RUN` steps run under `proot`, not a real container runtime: no PID,\nnetwork, or IPC isolation, no `cgroups`, no `seccomp`. Build steps\nthat depend on real namespaces or kernel features will fail or\nbehave subtly differently from a `docker build`. Multi-platform\nmanifest lists are not produced — build once per target architecture\nand use `--tag` to keep them distinct.\n\n---\n\n### `push` — Push a built image to a registry\n\n```\nproot-distro push [OPTIONS] IMAGE\n```\n\nUpload a locally built image to a Docker/OCI registry. The image must\nhave been produced by `proot-distro build -t IMAGE` first; `push`\nreads the manifest and all blobs directly from the local cache and\nstreams them to the registry. No Docker daemon and no on-disk\nintermediate archive are involved.\n\n`IMAGE` is the same reference passed to `build -t`. When the last\npath component lacks an explicit `:tag` suffix, `:latest` is appended\n(matching `build`'s behaviour). The registry is derived from the\nimage reference: a bare or user/repo form goes to Docker Hub, a host\nform like `ghcr.io/foo/bar:tag` goes to the named registry.\n\n**Options:**\n\n| Option | Description |\n|---|---|\n| `--architecture ARCH` | Push the manifest built for the given architecture. Accepts proot-distro names (`aarch64`, `arm`, `i686`, `riscv64`, `x86_64`) or Docker platform strings (`linux/arm64`, `linux/amd64`, …). Default: host architecture. The manifest cache is keyed by `(IMAGE, arch)`, so the value must match the architecture you built for. |\n| `-q`, `--quiet` | Suppress non-error output. |\n\n**Authentication:**\n\n`push` uses the same `PD_DOCKER_AUTH=username:password` contract as\n`install`. The colon separator is mandatory — the value is forwarded\nas HTTP Basic auth to the registry's token endpoint to obtain a\nscoped bearer token with `pull,push` actions:\n\n```sh\n# Docker Hub\nexport PD_DOCKER_AUTH=myuser:mypassword\nproot-distro push myuser/myapp:1.0\n\n# GitHub Container Registry — use a PAT with the write:packages scope\nexport PD_DOCKER_AUTH=myuser:ghp_xxx\nproot-distro push ghcr.io/myorg/myapp:1.0\n\n# Any other OCI registry\nexport PD_DOCKER_AUTH=myuser:mypassword\nproot-distro push registry.example.com/private/myapp:1.0\n```\n\nSelf-hosted registries that allow anonymous push do not need\n`PD_DOCKER_AUTH` set — the token endpoint returns an empty token and\nthe registry accepts unauthenticated `PUT`s.\n\n**Behaviour:**\n\n- Each layer is HEAD-probed against the registry first; blobs that\n  already exist are skipped, so re-pushing an unchanged image\n  transfers only the manifest.\n- New layers stream from disk via a single monolithic `PUT` per\n  blob — memory usage stays flat regardless of layer size.\n- The image config bytes are re-canonicalised from the cached dict\n  and their SHA-256 is verified to match the digest recorded in the\n  manifest before upload. A mismatch indicates a corrupted local\n  cache and aborts the push.\n- 401/403 responses (on any phase: token fetch, blob upload, manifest\n  PUT) are reported with a hint to set or fix `PD_DOCKER_AUTH`.\n\n**Examples:**\n\n```sh\n# Build then push to Docker Hub\nproot-distro build -t myuser/myapp:1.0 ./mycontext\nexport PD_DOCKER_AUTH=myuser:mypassword\nproot-distro push myuser/myapp:1.0\n\n# Build a cross-arch image and push it\nproot-distro build -t myuser/myapp:arm64 --architecture aarch64 .\nproot-distro push --architecture aarch64 myuser/myapp:arm64\n\n# Push to a self-hosted, unauthenticated registry\nproot-distro build -t registry.lan:5000/internal/tool:dev .\nproot-distro push registry.lan:5000/internal/tool:dev\n\n# Quiet mode (only errors print)\nproot-distro push -q myuser/myapp:1.0\n```\n\n**Limitations:**\n\nMulti-architecture manifest lists are not produced — each push writes\na single-arch manifest under the tag. To publish a multi-arch image,\nbuild and push each architecture separately; the tag is overwritten\non each push, so a higher-level tool would be required to assemble a\nmanifest list. Cross-repository blob mounting and chunked uploads are\nnot implemented; layers shared across repositories on the same\nregistry are re-uploaded in full, and a failed multi-gigabyte layer\nupload must restart from zero.\n\n---\n\n### `login` — Start a shell inside a container\n\n```\nproot-distro login [OPTIONS] CONTAINER [-- COMMAND ...]\nAliases: sh\n```\n\nSpawn an interactive shell (or a custom command) inside an installed\ncontainer. The `--` separator passes a command to run inside the\ncontainer's login shell (the shell still wraps it, so quoting and\nredirection inside `COMMAND` work as expected).\n\n**Examples:**\n\n```sh\n# Interactive shell as root\nproot-distro login ubuntu\n\n# Interactive shell as a non-root user\nproot-distro login ubuntu --user myuser\n\n# Run a single command\nproot-distro login ubuntu -- /bin/ls /etc\n\n# Run a shell command string\nproot-distro login ubuntu -- bash -c \"echo hello\"\n\n# Use the short alias\npd login ubuntu\n\n# Inspect the full proot command line without running it\nproot-distro login ubuntu --get-proot-cmd\n```\n\n**Options always available:**\n\n| Option | Description |\n|---|---|\n| `-u`, `--user USER` | Log in as USER (default: `root`). Accepts `name`, numeric `uid`, `name:group`, or `uid:gid`. For containers without `/etc/passwd`, only a numeric UID or the literal `root` is accepted (and a numeric GID when `:group` is given). |\n| `-P`, `--redirect-ports` | Redirect privileged ports 1–1023 to higher numbers (`22 → 2022`, `80 → 2080`, …). The offset is hardcoded inside `proot`. |\n| `--shared-home` | Bind the host home directory into the container (mounted at `/root` for the root user, at the user's home otherwise; for termux-type containers it goes to `/data/data/com.termux/files/home`). |\n| `--shared-tmp` | Bind the host `$PREFIX/tmp` to `/tmp` inside the container (Termux only; skipped for termux-type containers). |\n| `--shared-x11` | Bind `$PREFIX/tmp/.X11-unix` to `/tmp/.X11-unix` (Termux only; skipped for termux-type containers). |\n| `-b`, `--bind SRC[:DST]` | Bind a custom path (repeatable). `SRC` is resolved via `os.path.abspath`. `DST`, when given, must be an absolute path (relative destinations are rejected). Overlap with an existing destination emits a warning but the bind is still added. |\n| `--emulator PATH` | Override the QEMU emulator binary for cross-arch containers. PATH must be executable. Only QEMU user-mode and Blink are known to work. |\n| `--kernel STRING` | Customize the kernel release string reported by `uname -r`. Default: `6.17.0-PRoot-Distro`. |\n| `--hostname STRING` | Customize the hostname inside the container. Default: `localhost`. |\n| `-w`, `--work-dir PATH` | Set the initial working directory. Default: the user's home directory. |\n| `-e`, `--env VAR=VALUE` | Set an environment variable in the guest (repeatable). Wins over image-defined `Env` and the baseline defaults. |\n| `--get-proot-cmd` | Print the fully assembled `env` + `proot` command line (escaped, with line continuations) and exit without running. |\n\n**Options available only on Termux (Android):**\n\n| Option | Description |\n|---|---|\n| `--isolated` | Skip non-essential host bindings (Android system dirs, Termux `$HOME`, `/sdcard`, Termux app paths). Keeps the link2symlink/sysvipc/kill-on-exit proot extensions and the kernel-release override. Mutually exclusive with `--minimal`. |\n| `--minimal` | Bare-minimum proot: only `/dev`, `/proc`, `/sys` are bound, `--sysvipc` is disabled, no fake `/proc` stubs, no `--kernel-release`. Guest env contains only your `--env` entries plus `TERM`/`COLORTERM`. Mutually exclusive with `--isolated`. |\n| `--no-link2symlink` | Disable proot's hard-link emulation. Only safe on devices with SELinux in permissive mode. |\n| `--no-sysvipc` | Disable System V IPC emulation. Only useful on kernels that already implement it. |\n| `--no-kill-on-exit` | Wait for all child processes before exiting the session. |\n\n#### Host bindings (Termux, default mode)\n\nWithout `--isolated` or `--minimal`, the following host paths are\nmounted inside the container when present and readable:\n\n```\n/apex\n/data/app\n/data/dalvik-cache\n/data/misc/apexdata/com.android.art/dalvik-cache\n/data/data/\u003ctermux-app-package\u003e\n/linkerconfig/com.android.art/ld.config.txt\n/linkerconfig/ld.config.txt\n/odm\n/plat_property_contexts\n/product\n/property_contexts\n/sdcard\n/storage/emulated/0\n/storage/self/primary\n/system\n/system_ext\n/vendor\n```\n\nEach entry is filtered through `realpath` + permission check: traversable\ndirectories (modes `1`/`5`/`7`) and readable files are bound; anything\nelse is silently skipped.\n\nPlus, for normal-type containers, the Termux `$PREFIX` is bound at its\noriginal path inside the guest so Termux utilities (`termux-api`,\n`pkg`, etc.) are reachable.\n\n#### Guest environment\n\nThe host's environment is **not** carried into the guest. PRoot-Distro\nbuilds a clean environment dict and passes it to `os.execvpe(\"proot\",\n…)`. Precedence (later entries win):\n\n1. Baseline: `PATH` (from `DEFAULT_PATH_ENV`), `MOZ_FAKE_NO_SANDBOX=1`,\n   `PULSE_SERVER=127.0.0.1` (Termux only).\n2. Image-defined `Env` from `manifest.json`. Cannot override Android\n   system vars, `MOZ_FAKE_NO_SANDBOX`, `PULSE_SERVER`, `TERM`, or\n   `COLORTERM`.\n3. Android system vars (`ANDROID_*`, `BOOTCLASSPATH`, etc.), Termux\n   only, when not `--isolated` and not `--minimal`.\n4. Your `--env VAR=VALUE` entries.\n5. `HOME`, `USER`, `TERM` (defaulting to `xterm-256color`),\n   `COLORTERM` (only when set on the host).\n\nAfter the precedence pass, `$PREFIX/bin` is appended to `PATH` so\nTermux host tools stay reachable inside the guest. A snippet at\n`/etc/profile.d/termux-profile.sh` re-applies every login-time\nenvironment variable (PATH, image Env, Android system vars, `--env`\nflags) after the distro's `/etc/profile` resets the environment on\nlogin — without it, running `su - someuser` inside the container\nwould silently drop those values. Per-session vars (`HOME`, `USER`,\n`TERM`, `COLORTERM`) and proot-internal vars are excluded.\n\nIn `--minimal` mode steps 1–3 and the `PATH` post-processing are\nskipped; only your `--env` entries plus `TERM`/`COLORTERM` are\nexported.\n\n#### Legacy migration\n\nIf a container was created by an older version of PRoot-Distro and its\nrootfs is still at the legacy path\n(`$RUNTIME_DIR/installed-rootfs/\u003cname\u003e`), `login` automatically migrates\nit to the new layout (`$RUNTIME_DIR/containers/\u003cname\u003e/rootfs`) on first\nuse, including rewriting any internal proot link2symlink (l2s)\nsymlinks. This may take a while on large containers.\n\n---\n\n### `run` — Run the image-defined entrypoint\n\n```\nproot-distro run [OPTIONS] CONTAINER [-- ARG ...]\n```\n\nRun the `Entrypoint` and/or `Cmd` defined in the container's Docker\nimage manifest. This is equivalent to `docker run`: the container\nstarts, executes the image-defined command, and exits when it\nfinishes.\n\n`run` requires that the container was installed from an OCI image\n(plain tarball installs have no `manifest.json` and therefore no\nrecorded Entrypoint/Cmd).\n\n**Entrypoint and Cmd resolution:**\n\n| Image | Args after `--` | Inner command |\n|---|---|---|\n| `Entrypoint` + `Cmd` | _(none)_ | `Entrypoint + Cmd` |\n| `Entrypoint` + `Cmd` | `ARGS` | `Entrypoint + ARGS` (Cmd replaced) |\n| Only `Cmd` | _(none)_ | `Cmd` |\n| Only `Cmd` | `ARGS` | `ARGS` (Cmd replaced) |\n| Only `Entrypoint` | _(none)_ | `Entrypoint` |\n| Only `Entrypoint` | `ARGS` | `Entrypoint + ARGS` |\n| Neither | _(none)_ | Error |\n| Neither | `ARGS` | `ARGS` |\n\nWhen `--work-dir` is not given, `run` uses the image's `WorkingDir`\n(falling back to `/` if it is empty).\n\n`run` accepts the same options as `login` (`--user`, `--bind`,\n`--isolated`, `--minimal`, `--env`, `--shared-tmp`, `--shared-x11`,\n`--emulator`, `--get-proot-cmd`, etc.). See\n`proot-distro login --help`.\n\n**Examples:**\n\n```sh\n# Run the image's default entrypoint\nproot-distro run hello-world\n\n# Run with port redirection (so 80 → 2080)\nproot-distro run nextcloud --redirect-ports\n\n# Pass arguments to the entrypoint (overrides image Cmd)\nproot-distro run ubuntu -- /bin/echo hi\n\n# Print the proot command line without executing\nproot-distro run nextcloud --get-proot-cmd\n```\n\n---\n\n### `list` — List installed containers\n\n```\nproot-distro list\nAliases: li, ls\n```\n\nShow all installed containers (subdirectories of `containers/` that\nhave a `rootfs/`). When none are installed, an install suggestion is\nprinted.\n\n| Option | Description |\n|---|---|\n| `-q`, `--quiet` | Print only container names, one per line (different from the global `--quiet`). |\n\n---\n\n### `remove` — Delete a container\n\n```\nproot-distro remove [OPTIONS] CONTAINER\nAliases: rm\n```\n\nPermanently delete the specified container and all its data. **This\ncannot be undone and is not confirmed.** Permissions of chmod-000'd\nfiles are fixed on the fly so the rootfs can always be cleared.\n\n| Option | Description |\n|---|---|\n| `-v`, `--verbose` | Log each deleted file. |\n| `-q`, `--quiet` | Suppress non-error output. Mutually exclusive with `--verbose`. |\n\n---\n\n### `rename` — Rename a container\n\n```\nproot-distro rename OLDNAME NEWNAME\n```\n\nRename a container from `OLDNAME` to `NEWNAME`. Also rewrites every\nproot link2symlink (l2s) symlink whose target is still pointing into\nthe old rootfs path, so hardlinks remain valid. This may take a while\non large containers.\n\nFor data-integrity reasons, **CTRL-C and CTRL-\\\\ are intercepted**\nduring the l2s rewrite. The signal is replaced with a one-line warning;\nthe rewrite continues until done.\n\n| Option | Description |\n|---|---|\n| `-q`, `--quiet` | Suppress non-error output. |\n\n---\n\n### `reset` — Reinstall a container from scratch\n\n```\nproot-distro reset CONTAINER\n```\n\nRemove the container rootfs and reinstall it from the Docker image\nrecorded at install time. **All data inside the container is lost.**\n\nThe image reference and target architecture are read from\n`containers/\u003cname\u003e/manifest.json`. If that file is missing, the command\nexits with an error — reset is supported for OCI image installs only\n(plain rootfs tarballs cannot be re-pulled).\n\n| Option | Description |\n|---|---|\n| `-q`, `--quiet` | Suppress non-error output. |\n\n---\n\n### `backup` — Archive a container\n\n```\nproot-distro backup [OPTIONS] CONTAINER\nAliases: bak, bkp\n```\n\nCreate a TAR archive of the container. The archive contains\n`\u003cname\u003e/manifest.json` (when present) and `\u003cname\u003e/rootfs/`.\n\n**Options:**\n\n| Option | Description |\n|---|---|\n| `-o`, `--output FILE` | Write to FILE instead of stdout. Refuses to overwrite an existing file. |\n| `-c`, `--compress TYPE` | Force compression: `gzip`, `bzip2`, `xz`, or `none`. Overrides extension-based detection. |\n| `-v`, `--verbose` | Log each archived file. |\n| `-q`, `--quiet` | Suppress non-error output. Mutually exclusive with `--verbose`. |\n\nWhen `--output` is given, the compression algorithm is inferred from\nthe file extension (`.tar.gz`, `.tgz`, `.tar.bz2`, `.tbz2`, `.tar.xz`,\n`.txz`, `.tar.lzma`, `.tlzma`, or plain `.tar`) unless `--compress`\noverrides it. Unsupported extensions (`.tar.zst`, `.tzst`, `.tar.lz4`,\n`.tar.lz`) are rejected.\n\nWithout `--output`, the archive is written to stdout, uncompressed by\ndefault. Stdout cannot be a TTY (you must redirect or pipe).\n\nFile ownership in the archive is **zeroed** (uid=gid=0, no\nuname/gname). Block devices, character devices, FIFOs, and sockets are\nsilently skipped. Symlinks to directories are stored as single entries.\nBefore archiving, the rootfs permissions are fixed up so chmod-000'd\nsubtrees become at least readable by the owner.\n\n`backup` is **TTY-safe** when piping into an interactive consumer\n(e.g. `gpg -c` with a pinentry prompt): all progress output is\nsuppressed while the downstream process holds the TTY in\nnon-canonical/no-echo mode, then resumes once the TTY returns to\nnormal.\n\n**Examples:**\n\n```sh\n# Create a compressed backup\nproot-distro backup ubuntu --output ubuntu.tar.xz\n\n# Pipe to another command\nproot-distro backup ubuntu | gzip \u003e ubuntu.tar.gz\n\n# Encrypt with GPG (pinentry-safe)\nproot-distro backup ubuntu | gpg -c \u003e ubuntu.tar.gpg\n\n# Verbose listing while archiving\nproot-distro backup ubuntu --output ubuntu.tar --verbose\n```\n\n---\n\n### `restore` — Restore a container from a backup\n\n```\nproot-distro restore [OPTIONS] [BACKUP_FILE]\n```\n\nRestore a container from a TAR archive. When `BACKUP_FILE` is omitted,\narchive data is read from stdin.\n\nCompression is detected automatically — `tarfile`'s `r|*` auto-detect\nhandles file input; for stdin, the first 6 magic bytes are peeked to\nidentify gzip / bzip2 / xz / lzma streams.\n\n**Options:**\n\n| Option | Description |\n|---|---|\n| `-v`, `--verbose` | Log each extracted file. |\n| `-q`, `--quiet` | Suppress non-error output. Mutually exclusive with `--verbose`. |\n\n**Archive format requirements:**\n\n- Files must be stored under a subdirectory named after the container\n  (`\u003cname\u003e/manifest.json`, `\u003cname\u003e/rootfs/...`). Bare-root archives\n  (e.g. `tar` of a rootfs without any leading directory) are rejected.\n- If `manifest.json` is not present in the archive, the container is\n  restored without it (login still works, but `reset` and `run` will\n  not).\n- Legacy archives (`installed-rootfs/\u003cname\u003e/...`) are accepted and\n  automatically re-rooted to the new layout.\n\nThe existing rootfs for each container in the archive is cleared\nrecursively on the first entry seen for that container. Hard links\ninside the archive are resolved against the archive's own paths and\nmaterialised as independent file copies (via `shutil.copy2`) rather\nthan real hard links, because the on-disk rootfs uses proot's\nlink2symlink emulation and a host-level hard link would alias what\nthe guest treats as separate inodes.\n\n`restore` is **TTY-safe** when reading from a pipe that involves an\ninteractive producer (`gpg -d archive.gpg | proot-distro restore`):\nprogress output stays silent while the upstream pinentry holds the\nTTY, then resumes once the TTY returns to normal.\n\n**Examples:**\n\n```sh\n# Restore from a file\nproot-distro restore ubuntu.tar.xz\n\n# Restore from stdin\ncat ubuntu.tar.xz | proot-distro restore\n\n# Decrypt + restore in one pipeline\ngpg -d ubuntu.tar.gpg | proot-distro restore\n```\n\n---\n\n### `copy` — Copy files to or from a container\n\n```\nproot-distro copy [OPTIONS] [CONTAINER:]SRC [CONTAINER:]DEST\nAliases: cp\n```\n\nCopy files between the host filesystem and a container rootfs, or\nbetween two containers. Paths inside a container are prefixed with the\ncontainer name and a colon: `ubuntu:/etc/resolv.conf`.\n\n| Option | Description |\n|---|---|\n| `-r`, `--recursive` | Copy directories recursively (preserves symlinks). |\n| `-m`, `--move` | Move instead of copying (deletes source after success). |\n| `-v`, `--verbose` | Log each copied file. |\n| `-q`, `--quiet` | Suppress non-error output. Mutually exclusive with `--verbose`. |\n\nDirectories `.` and `..` are accepted only as source, not as\ndestination. Glob patterns are not supported (rely on the shell).\n\n**Examples:**\n\n```sh\n# Copy a local file into a container\nproot-distro copy ./file.txt ubuntu:/root/file.txt\n\n# Copy a file out of a container\nproot-distro copy ubuntu:/etc/resolv.conf ./resolv.conf.bak\n\n# Copy between two containers\nproot-distro copy arch:/etc/pacman.conf ubuntu:/tmp/pacman.conf\n\n# Recursive copy of a local directory\nproot-distro copy --recursive ./myapp ubuntu:/opt/myapp\n```\n\n---\n\n### `sync` — Synchronize files to or from a container\n\n```\nproot-distro sync [OPTIONS] [CONTAINER:]SRC [CONTAINER:]DEST\n```\n\nSynchronize SRC to DEST, copying only files that differ. Both paths\nmay be plain host paths or `container:path` references. Always\nrecursive — no flag needed.\n\n**Comparison method:**\n\n| Mode | What is compared |\n|---|---|\n| Default | File size and integer modification time |\n| `--checksum` | File size and CRC32 checksum |\n\n**Behavior:**\n\n- Symlinks are copied as-is (target not followed).\n- Hard links become independent file copies (no inode tracking).\n- Block/char devices, FIFOs, and sockets are silently skipped.\n- File ownership is never changed (`chown` is not called).\n- Access modes and modification timestamps are preserved.\n- Regular files are written atomically (`.~pd_sync` temp file →\n  `os.replace`) so a partial copy never leaves a corrupt destination.\n- If a source file is not readable, a warning is printed and it is\n  skipped.\n- If the destination lacks write permission, `sync` first attempts to\n  `chmod` it. If that also fails, the command exits with an error.\n\n| Option | Description |\n|---|---|\n| `-c`, `--checksum` | Compare by size + CRC32 instead of size + mtime (slower, more strict). |\n| `-d`, `--delete` | Remove destination files and directories that have no counterpart in the source. Applied after the sync pass; only effective when source is a directory. |\n| `-v`, `--verbose` | Log each synced or deleted entry. Suppresses the progress bar. |\n| `-q`, `--quiet` | Suppress non-error output. Mutually exclusive with `--verbose`. |\n\n**Examples:**\n\n```sh\n# Sync a local directory into a container\nproot-distro sync ./app ubuntu:/opt/app\n\n# Sync a directory out of a container\nproot-distro sync ubuntu:/etc ./backup/etc\n\n# Use checksum-based comparison\nproot-distro sync --checksum ./data ubuntu:/data\n\n# Make destination match source exactly (delete extras)\nproot-distro sync --delete ./app ubuntu:/opt/app\n```\n\n---\n\n### `clear-cache` — Delete the download cache\n\n```\nproot-distro clear-cache\nAliases: clear, cl\n```\n\nRemove every entry from `$BASE_CACHE_DIR` — registry-pulled and\nbuild-produced layer blobs (`oci_layers/`), resolved single-arch\nmanifests (`oci_manifests/`), and the build cache index\n(`build_cache_index.json`) all go in one pass. Disk space freed is\nreported after the operation in human-readable units.\n\n| Option | Description |\n|---|---|\n| `-v`, `--verbose` | Log each deleted file. |\n| `-q`, `--quiet` | Suppress non-error output. Mutually exclusive with `--verbose`. |\n\nAfter `clear-cache`, the next `install` (or `reset`) of an image\nrequires network access again, and layers must be re-downloaded and\nre-verified.\n\n---\n\n## How PRoot-Distro works\n\nPRoot-Distro is a thin orchestration layer around two primary building\nblocks:\n\n### 1. OCI registry client\n\nThe `install` command speaks the standard OCI Distribution protocol\ndirectly over `urllib`:\n\n- Public images on **Docker Hub** require no flags\n  (e.g. `ubuntu:24.04`).\n- Public images on **other registries** are addressed by full reference\n  (e.g. `ghcr.io/myorg/myimage:tag`). PRoot-Distro probes\n  `https://\u003cregistry\u003e/v2/`, follows the OCI Bearer auth challenge, and\n  pulls an anonymous token.\n- Manifest lists are resolved to the platform that matches your CPU\n  (or the `--architecture` override).\n- Each layer blob is downloaded with its **SHA-256 verified** before\n  being promoted into the cache. Cross-host redirects (Docker Hub →\n  CDN) have their `Authorization` header stripped to satisfy CDNs that\n  reject bearer tokens.\n- Layer blobs and the resolved single-arch manifest are cached locally.\n  Subsequent installs of the same image are fully offline.\n\nLayers are applied in order on top of an empty rootfs directory, with\nfull OCI whiteout semantics (`.wh..wh..opq` and `.wh.\u003cname\u003e` markers).\nHard links are materialised as copies for self-containment. Block\ndevices, character devices, and FIFOs are skipped.\n\nAfter all layers are applied, PRoot-Distro adds three small fixups when\nthe image has `/etc/`:\n\n- `/etc/resolv.conf` is replaced with Google DNS (8.8.8.8 / 8.8.4.4).\n- `/etc/hosts` is populated with a minimal localhost mapping.\n- The host's Termux/Android user is registered as `aid_\u003cname\u003e` in\n  `/etc/passwd`, `/etc/shadow`, `/etc/group`, and `/etc/gshadow` so\n  Android's UID-based permissions work inside the container.\n\nThe full OCI manifest and image config are saved to\n`containers/\u003cname\u003e/manifest.json`. This lets `reset` re-pull the\nexact same image later, and `run` know what to execute by default.\n\nA **local archive** also can be used with `install` command:\n\n- A **plain rootfs tarball** (`alpine.tar.gz`, `debian.tar.xz`, etc.):\n  the leading path component count is detected automatically by\n  scoring directory names like `bin`, `usr`, `etc`, `var`.\n- An **OCI image layout** (as produced by `docker save` or\n  `skopeo copy oci-archive:`): detected by the presence of an\n  `oci-layout` file at the archive root. The selected platform's layers\n  are extracted into the cache and applied with the same code path used\n  for registry pulls.\n\n### 2. The proot utility\n\n[proot](https://proot-me.github.io/) is a user-space implementation of\n`chroot`, `mount --bind`, and `binfmt_misc`. It uses Linux's `ptrace`\nmechanism to intercept system calls made by the guest process and\nrewrite filesystem paths on the fly. The result is a chroot-like\nenvironment that does not need root privileges.\n\nWhen you run `proot-distro login ubuntu`, PRoot-Distro `exec`s into a\n`proot` command line that looks roughly like:\n\n```sh\nenv PATH=… HOME=/root … \\\n  proot --kill-on-exit --link2symlink --sysvipc \\\n        --kernel-release=… -L \\\n        --change-id=0:0 \\\n        --rootfs=/…/containers/ubuntu/rootfs --cwd=/root \\\n        --bind=/dev --bind=/proc --bind=/sys \\\n        --bind=/storage --bind=/system --bind=/apex … \\\n        /bin/sh -l\n```\n\nYou can see this exact command for any container by adding the\n`--get-proot-cmd` flag to `login` or `run`.\n\n#### Cross-architecture support\n\nArchitectures supported as containers: `aarch64`, `arm`, `i686`,\n`x86_64`, `riscv64`. The host CPU is detected via `os.uname().machine`.\nCross-architecture execution uses **QEMU user-mode** via proot's `-q`\nflag. The matching `qemu-user-\u003carch\u003e` package must be installed on the\nhost. 32-bit guests run natively on 64-bit hosts when the kernel\nsupports `PER_LINUX32` (probed via `ctypes`).\n\nContainer architecture is detected automatically at every login by\nreading ELF headers of common shell binaries inside the rootfs — there\nis no separate config file to remember.\n\n---\n\n## Storage layout\n\nAll runtime data is stored under `$RUNTIME_DIR`:\n\n- **Termux**: `$TERMUX__PREFIX/var/lib/proot-distro/`, where\n  `TERMUX__PREFIX` defaults to `/data/data/com.termux/files/usr`.\n- **Regular Linux**: `$XDG_DATA_HOME/proot-distro/` (default\n  `~/.local/share/proot-distro/`).\n\nThe OCI download cache (`$BASE_CACHE_DIR`) is under `$RUNTIME_DIR`\non Termux, and under `$XDG_CACHE_HOME/proot-distro/` (default\n`~/.cache/proot-distro/`) on a regular Linux host.\n\nAll paths below are relative to `$RUNTIME_DIR` unless noted; cache\npaths sit under `$BASE_CACHE_DIR` (`$RUNTIME_DIR/cache` on Termux,\n`$XDG_CACHE_HOME/proot-distro/` elsewhere).\n\n| Path | Contents |\n|---|---|\n| `containers/\u003cname\u003e/rootfs/` | Container root filesystem |\n| `containers/\u003cname\u003e/manifest.json` | Image reference, arch, full OCI manifest, full image config |\n| `containers/\u003cname\u003e/rootfs/.l2s/` | Proot link2symlink (l2s) backing store (created on first login) |\n| `locks/\u003cname\u003e.lock` | Per-container POSIX flock (shared for `login`/`run`, exclusive for `install`/`remove`/…) |\n| `locks/build/\u003csha256-prefix\u003e.lock` | `BuildLock` keyed on `(image_ref, arch)` for `build` and `push` |\n| `$BASE_CACHE_DIR/oci_layers/` | Cached OCI layer blobs (registry pulls **and** `build` outputs) |\n| `$BASE_CACHE_DIR/oci_manifests/` | Cached resolved single-arch manifests (registry pulls **and** `build -t` tags) |\n| `$BASE_CACHE_DIR/build_cache_index.json` | `build` cache index: recipe-hash → layer-digest |\n| `installed-rootfs/\u003cname\u003e/` | **Legacy** layout; auto-migrated by `login`. |\n\n---\n\n## Environment variables\n\n| Variable | Effect |\n|---|---|\n| `TERMUX__PREFIX` | Override Termux prefix path; drives `PREFIX` and `RUNTIME_DIR` on Termux. Defaults to `/data/data/com.termux/files/usr`. |\n| `TERMUX__HOME` | Override the Termux home path used for `--shared-home` and the default storage bindings. Defaults to `/data/data/com.termux/files/home`. |\n| `TERMUX_APP__PACKAGE_NAME` | Override the Termux app package (default `com.termux`); used for `--bind=/data/data/\u003cpkg\u003e/...`. |\n| `TERMUX_APP__APP_VERSION_NAME`, `TERMUX_VERSION` | Either one (when set) counts as one of the indicators that flips on Termux mode in `_detect_termux()`. |\n| `XDG_DATA_HOME` | On non-Termux hosts, base for `$XDG_DATA_HOME/proot-distro/`. Defaults to `~/.local/share`. |\n| `XDG_CACHE_HOME` | On non-Termux hosts, base for `$XDG_CACHE_HOME/proot-distro/`. Defaults to `~/.cache`. |\n| `PD_DOCKER_AUTH` | Credentials for pulling and pushing Docker/OCI images. Must be in `username:password` or `username:PAT` format (colon required). Sent as HTTP Basic auth to the registry's token endpoint to obtain a scoped bearer token. Takes effect for `install`, `build` (`FROM` base-image pulls), and `push` (with `pull,push` scope). |\n| `PD_FORCE_NO_COLORS` | When set to any value, disables ANSI colors in PRoot-Distro's own output. |\n| `PROOT_NO_SECCOMP` | Inherited and forwarded to `proot`. Set to `1` if `login` fails with seccomp-related errors on the host kernel. Skipped in `--minimal` mode. |\n| `PROOT_VERBOSE` | Inherited and forwarded to `proot` for debugging. Skipped in `--minimal` mode. |\n| `COLUMNS` | Fallback terminal width for `--help` rendering. |\n| `TERM`, `COLORTERM` | Inherited from the host and exported into the guest (always; even in `--minimal`). In `normal`-type containers, `TERM` defaults to `xterm-256color` when unset on the host. |\n\n---\n\n## Shell completions\n\nThe packaged distribution installs completion scripts for Bash, Zsh,\nand Fish to the standard locations:\n\n- `share/bash-completion/completions/proot-distro`\n- `share/zsh/site-functions/_proot-distro`\n- `share/fish/vendor_completions.d/proot-distro.fish`\n\nAll three scripts complete both `proot-distro` and the short alias `pd`.\n\nIf your shell does not pick them up automatically, copy them by hand:\n\n```sh\n# Bash, current user\nmkdir -p ~/.local/share/bash-completion/completions\ncp proot_distro/completions/proot-distro.bash \\\n   ~/.local/share/bash-completion/completions/proot-distro\n\n# Zsh, current user\nmkdir -p ~/.zsh/completions\ncp proot_distro/completions/_proot-distro ~/.zsh/completions/_proot-distro\n# and add 'fpath=(~/.zsh/completions $fpath)' to .zshrc before compinit\n\n# Fish, current user\nmkdir -p ~/.config/fish/completions\ncp proot_distro/completions/proot-distro.fish \\\n   ~/.config/fish/completions/proot-distro.fish\n```\n\n---\n\n## Limitations\n\n### PRoot limitations\n\n- **Performance**: `proot` intercepts every system call via `ptrace`.\n  Filesystem-heavy workloads (compilation, package managers) are\n  noticeably slower than native execution.\n- **Kernel features**: features that depend on Linux kernel modules\n  (FUSE, specific iptables targets, custom cgroup hierarchies, etc.)\n  do not work.\n- **No real root**: proot uses UID/GID remapping to fake root. Programs\n  that genuinely need kernel-level root (`sudo`, `mount` of real\n  filesystems, `iptables`, etc.) will fail.\n- **No background services**: starting service supervisors (`systemd`,\n  `OpenRC`, socket-activated daemons) is generally not possible. You\n  can run individual long-running processes, but a full init system is\n  out of scope.\n- **No cgroups / namespaces**: features that need real Linux kernel\n  namespaces (`unshare`, container-in-container, network namespaces)\n  do not work — proot is path translation, not kernel isolation.\n- **seccomp**: some Android kernels restrict `ptrace` calls used by\n  proot via seccomp policies. If `login` fails with seccomp-related\n  errors, set `PROOT_NO_SECCOMP=1`.\n- **No nesting**: PRoot-Distro refuses to run inside another `proot`,\n  because `proot` itself cannot trace a process already being traced.\n\n### PRoot-Distro limitations\n\n- **Registry authentication**: pulling private images and pushing to\n  private repositories require setting\n  `PD_DOCKER_AUTH=user:password` (or `user:personal-access-token`).\n  Credential helpers and Docker config file (`~/.docker/config.json`) are\n  not read — only the environment variable is supported.\n- **No zstd-compressed layers**: Python's `tarfile` module does not\n  support zstd. Images using zstd-compressed layers (some newer Docker\n  Hub images) fail to install with an explicit error. Try a different\n  image or an older tag.\n- **Image building runs under proot, not BuildKit**: `proot-distro\n  build` produces OCI/Docker-compatible images, but each `RUN` step\n  executes under `proot` (no PID/network/IPC isolation, no `cgroups`,\n  no `seccomp`). BuildKit-only Dockerfile features\n  (`RUN --mount=type=cache|secret|ssh`, `RUN --network`,\n  `RUN --security=insecure`, `COPY --link`, `COPY --parents`) are\n  rejected with an explicit error. Multi-platform manifest lists are\n  not produced — build (and `push`) once per architecture. For complex\n  builds that depend on a real container runtime, use `docker build` /\n  `buildah` / `nerdctl` on a full host and `proot-distro install\n  ./myimage.oci.tar`.\n- **`push` is single-arch, single-stream**: each `proot-distro push`\n  writes a single-arch manifest, with no manifest-list assembly,\n  cross-repository blob mounting, or chunked uploads. Layers shared\n  across repos on the same registry are re-uploaded in full, and a\n  failed upload of a large layer must restart from zero.\n- **No live state migration**: `backup`/`restore` archive the rootfs\n  and the OCI manifest, but in-memory state of running processes is\n  not preserved.\n- **Cross-architecture Termux-type containers are not supported**: the\n  host and the container share the same Termux prefix path, so QEMU\n  emulation cannot hide the host's architecture-specific binaries.\n- **Termux-only flags on non-Termux hosts**: `--isolated`, `--minimal`,\n  `--no-link2symlink`, `--no-sysvipc`, and `--no-kill-on-exit` are not\n  exposed by argparse when running outside Termux. Most are\n  Android-specific in spirit, and on a regular Linux host the default\n  behavior is already isolated in the sense that there are no Android\n  bindings to drop.\n\n---\n\n## Donate\n\nSupport is important to keep the project up in a long term. I'm grateful for any amount of tip in cryptocurrency:\n\n**Bitcoin**:\n```\nbc1qxuwtc0sfjt43n3sufck6s0gaeand8eaeguajxs\n```\n\n**Ethereum**:\n```\n0x1F5196A5b0120D4a66FCAABBe71728239B06EC12\n```\n\n**Tron**:\n```\nTEJiwRMMGV1JXvRYDRVJ1qw7kFgskEk3sJ\n```\n\nRecipient: the author of PRoot-Distro, [@sylirre](https://github.com/sylirre)\n\n## Issues and contributing\n\n- **Bug reports**: https://github.com/termux/proot-distro/issues\n- **License**: GPL-3.0-only. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftermux%2Fproot-distro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftermux%2Fproot-distro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftermux%2Fproot-distro/lists"}