Polythene exposes two Cyclopts commands. Both commands accept the optional
--store argument to override the root filesystem directory (defaults to
/var/tmp/polythene).
Execution environments
Before running commands, decide how they interact with your host. Polythene provides a single interface but adapts to the available tooling:
- Codex sandbox – You run as root without container engines. The
polythene pullcommand still uses Podman inside the sandbox because Codex maintains the binary on disk, andpolythene execfalls back tobwrap,proot, or a privilegedchroot. - GitHub runners – You run as an unprivileged user with Podman available.
The CLI exports images in the same way but typically selects
bwrapfor execution, providing equivalent isolation to the Codex workflow.
In both environments the exported root filesystem is disposable, enabling repeatable system-level tests without polluting the host.
`polythene pull`
uv run polythene pull docker.io/library/busybox:latest
# or
python -m polythene pull docker.io/library/busybox:latest
The pull command:
- ensures the store directory exists,
- probes the active Podman store with
podman image exists IMAGE, - pulls the image only when that probe reports it absent,
- exports the image into a UUID-named root filesystem directory, and
- prints the generated UUID to stdout for later reuse, as the only line written there.
Podman's progress reporting, image IDs, and container IDs are kept off stdout,
so polythene pull can be captured with $(...) without filtering. It works
the same way with --timeout as without it. To see what Polythene and Podman
are doing, set POLYTHENE_VERBOSE (see
Environment variables) or read stderr.
Because the probe reads local storage rather than a registry, an image you
built in the same job can be reused without publishing it. The probe and the
subsequent container creation resolve the same podman command and inherit the
same environment, so both see the same Podman store:
podman build --tag localhost/example:latest .
uv run polythene pull localhost/example:latest --store ./polythene-store
Without the probe, Podman would read the localhost component of that
reference as a registry host and try to reach https://localhost/v2/, which
fails when no registry is listening there.
An image is only reused when it is visible to the Podman user and storage
configuration Polythene inherits. Building it with a different
--storage-driver, a different CONTAINERS_STORAGE_CONF, or under another
user places it in a separate store, and the probe will report it absent so
Polythene falls back to pulling it. The same applies when a probe fails for an
unrelated reason such as unreadable local storage: Polythene reports that
status instead of silently treating the image as missing.
Podman must be installed and available on PATH. When the POLYTHENE_VERBOSE
variable is set, the command also prints progress messages to stderr.
`polythene exec`
uv run polythene exec <uuid> -- uname -a
# or
python -m polythene exec <uuid> -- uname -a
Replace <uuid> with the value returned by a previous polythene pull call.
The exec command runs a user supplied program inside the exported root
filesystem. It picks the first available execution backend in the following
order:
Each backend receives the prepared filesystem as its root and blocks network access. If none of the backends is available, the command fails with an error message detailing the missing tooling.
When a specific backend is preferable, pass --isolation <backend> (or the
equivalent --isolation=<backend> form) to reorder the probing sequence.
GitHub runners lack user namespace support for bwrap, and specifying
--isolation proot avoids the noisy permission errors emitted by bubblewrap
before proot succeeds.
Because the same UUID works across hosts, you can prepare an image on Codex and reuse it on CI:
uv run polythene pull registry.example.invalid/tools:latest
uv run polythene exec <uuid> -- make -C /workspace/tests system
The exec invocation will select the available backend on each host while the
commands run unchanged.