Convert a Docker Compose file into a POSIX sh script that runs its services as a single Podman pod.
Built for CI and test environments where you can't use docker compose or podman kube play:
- No bridge networking / netavark. Unprivileged CI containers often have a read-only
/proc/sys, so netavark fails to create bridge networks. A single pod shares one network namespace with no bridge: services talk over127.0.0.1, and names resolve via--add-host. - No systemd. Podman healthchecks are normally scheduled by systemd timers. compose2pod gates startup by polling
podman healthcheck rundirectly, sodepends_on: service_healthyworks without systemd. - No heavy runtime. The core is stdlib-only — no dependencies, no compiled wheels — so it installs and runs in minimal Python images.
Requirements
Podman >= 6.0.0. Earlier releases have a bug where a container stopping
inside a multi-container pod wipes /etc/hosts for every container in that
pod, not just the one that stopped — fixed in 6.0.0. compose2pod's generated
scripts rely on one shared --add-host-populated /etc/hosts for the whole
pod (see architecture/supported-subset.md), so a service_completed_successfully
dependency (a container that runs and exits, e.g. a migration step) can wipe
name resolution for everything started after it on a pre-6.0.0 Podman.
Install
pip install compose2pod # core: reads compose as JSON
pip install compose2pod[yaml] # optional: read YAML directly (adds PyYAML)
Usage
# YAML directly (needs the [yaml] extra)
compose2pod docker-compose.yml --target app --image myimage:ci > run.sh
# Or stay dependency-free by piping JSON (e.g. via yq)
yq -o=json '.' docker-compose.yml | compose2pod --target app --image myimage:ci > run.sh
sh ./run.sh
Supported compose subset
compose2pod refuses every document docker compose config refuses — a
measured property, checked continuously by a differential conformance harness
that runs the real Docker CLI and the real compose2pod pipeline over the same
YAML. So a file that compiles is a file Docker would run; where compose2pod
still refuses a form Docker accepts, it is because Podman genuinely cannot
express it (each such case is documented, not guessed).
Within that boundary it covers most of what real compose files use:
- Services —
image/build,command/entrypoint,environmentandenv_file(string and long-form{path, required, format}),volumes(short-form and long-form--mount, including thebind/volume/tmpfsoption maps),tmpfs,healthcheck,depends_on(all conditions), networkaliases,hostname/container_name. - Confinement & metadata —
user,working_dir,read_only,init,privileged,cap_add/cap_drop,security_opt,devices,group_add,platform,labels,annotations,pull_policy(the quoted-boolean and YAML-1.1 spellings Docker accepts, too). - Resources — the legacy keys (
mem_limit,cpus,pids_limit,ulimits, …) and the moderndeploy.resourcesblock. - Pod-wide —
dns/dns_search/dns_opt,sysctls,extra_hosts. - Composition — same-file
extends,secrets/configs,profiles.
Compose extension fields (any x--prefixed key) and YAML anchors are accepted
as-is, so a top-level x-* anchor block for shared config just works.
${VAR}-style variable interpolation is left live in the generated script,
resolved by its shell against the environment present when the script runs (no
.env file support). See architecture/supported-subset.md for the full
accept/ignore/reject matrix and planning/decisions/ for the boundary rulings.
Status
Beta. Part of the modern-python family. MIT licensed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file compose2pod-0.3.1.tar.gz.
File metadata
- Download URL: compose2pod-0.3.1.tar.gz
- Upload date:
- Size: 57.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
48c4ac3571f678cd17d74aa374195e9a7b12bc98b6f75a1cb6a28c5daedda2d0
|
|
| MD5 |
f409a2e6c029038dc7dd3d1db57087de
|
|
| BLAKE2b-256 |
db319f33e9ea8aaa196dfa8c0683e33945b9884fb433c03a300a631665c1b998
|
File details
Details for the file compose2pod-0.3.1-py3-none-any.whl.
File metadata
- Download URL: compose2pod-0.3.1-py3-none-any.whl
- Upload date:
- Size: 65.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8cd8d6a38c62a6dbfe819f72a86d6025cf85faaa3302fb2d05cba4c2ec89a09
|
|
| MD5 |
b587406e379ed55f51e623a1ecb3b0c1
|
|
| BLAKE2b-256 |
0ee0182decfc6783f4fd1bd9ceb21aa6065305ef3d768e3a5aecdf099c44ba32
|