You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Define improvements to compose manifest workloads when targeting typical targets(Docker/Podman) #168
Improve guidance and normative requirements for Application Developers authoring Compose‑defined workloads such that a single Compose file is portable and deployable across:
Compose‑Docker–based devices
Compose‑Podman–based devices (including podman compose and podman‑compose)
This feature ensures Compose workload definitions are runtime‑agnostic, deterministic, and compatible with common industrial edge device constraints.
Provide adequate technical acceptance criteria(s) associated with this feature below:
Further clarify Compose defined workloads to ensure definitions are compatible across both Compose-docker and Compose-podman device types.
Define requirements to ensure the compose file is compatible across both types of deployments.
Although not required, it is highly encouraged to provide feature use-cases below:
Podman is a common device implementation for standalon devices, ensure our compose defined workloads can be deployed to both Compose-Docker and Compose-Podman implementations.
Additional information
Items that were brought up within the Plugfest:
Fully qualified image names
Docker accepts fully qualified image names without issue.
Podman requires or strongly prefers them for deterministic and secure pulls.
Using fully qualified names improves portability rather than creating divergence.
Avoid container_name unless truly required
Docker works correctly without container_name.
Podman behaves more predictably without it, especially when using podman‑compose.
Avoiding explicit container names removes lifecycle and naming collisions without breaking Docker behavior.
changed the title [-]Define improvements to compose defined workloads when targeting typical targets[/-][+]Define improvements to compose manifest workloads when targeting typical targets[/+]on Mar 19, 2026
changed the title [-]Define improvements to compose manifest workloads when targeting typical targets[/-][+]Define improvements to compose manifest workloads when targeting typical targets(Docker/Podman)[/+]on Mar 20, 2026
This is a corollary feature to have support for a common docker-compose yaml file suitable for Podman and Docker Compose.
However, we first need to add support for Podman as a runtime in Margo.
Currently we support only Docker Compose and Kubernetes as runtimes.
Use this reference table to design or refactor your compose.yaml (or docker-compose.yml) workloads for dual portability across Docker Compose and Podman (including podman-compose).
Image & Registry
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
image: short-name (e.g., mysql:latest)
Allowed
Ambiguous / Fails in CI
Do not use. Docker defaults to Docker Hub. Podman uses a registry alias file and prompts interactively when a short name is ambiguous — in non-interactive CI/CD mode this becomes a hard failure. Fix: Use a fully qualified image name (FQIN) such as docker.io/library/mysql:latest. FQIN is a universal portability requirement, not a Podman-specific workaround.
pull_policy:
Allowed
Allowed
Set explicitly for edge deployments. Use pull_policy: if_not_present to prevent unnecessary pull attempts on devices with intermittent connectivity. Without this, behaviour differs between runtimes and versions — Docker uses cached images by default, Podman's behaviour depends on the --pull flag. Explicit policy makes offline behaviour deterministic on both.
Container Identity & Naming
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
container_name
Allowed
Supported but limited
Avoid. Hardcoded container names conflict with --scale functionality and can clash with pod naming conventions when using podman-compose with pod arguments. Let the engine generate names automatically. Neither runtime requires explicit container names for inter-service communication — use service names instead.
Variables like ${COMPOSE_PROJECT_NAME} in container_name
Allowed
Unreliable
Avoid variable interpolation in container_name. Docker evaluates project variables dynamically. podman-compose does not reliably support string interpolation for all fields, particularly container_name. Use static values.
Ports & Networking
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
ports: - "8080:80"
Allowed
Allowed
Always wrap port mappings in quotation marks. Podman interprets naked numeric blocks strictly and can fail or misbind unexposed port ranges. Quoted strings are unambiguous on both runtimes.
network_mode: "host"
Allowed
Allowed
Supported on both, but rootless Podman cannot bind to privileged ports (below 1024) unless the host's unprivileged port range is adjusted via net.ipv4.ip_unprivileged_port_start. Plan port assignments accordingly.
networks: (Bridge)
Allowed
Allowed
Always define explicit user-named bridge networks. Do not rely on the default network. Explicit user-defined bridges ensure consistent container-to-container DNS resolution across both engines. Default network behaviour differs between Docker and podman-compose.
ipc: host
Allowed
Allowed
Supported on both platforms, but behaves differently. In rootless Podman, ipc: host shares only the IPC namespace of the non-root user who started the engine — not the entire host IPC namespace. Verify your application's IPC requirements against this constraint.
Volumes & Storage
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
volumes: (Named) — persistent data
Allowed
Allowed
Prefer named volumes over host bind mounts for all persistent application data. Named volumes are managed by the container engine, are portable across both Docker and Podman without SELinux or path-mapping concerns, and survive container recreation cleanly. This is the recommended pattern for persistent state on edge devices.
volumes: (Host Bind Mount)
Allowed
Requires flags
Add SELinux relabelling flags. Podman defaults to rootless mode and maps host folders differently into containers. Fix: Append :Z (private, unshared) or :z (shared) flags — e.g., ./data:/app/data:Z — to correctly remap SELinux labels and permissions. Without these flags, the container may be denied read/write access on SELinux-enforcing hosts.
Rootless UID/GID mapping for volumes
Allowed (root)
Requires attention
Understand UID mapping in rootless Podman. When a rootless Podman container writes to a named volume or bind mount, the UID inside the container may not match the UID on the host due to user namespace remapping, causing permission failures. Use userns_mode: "keep-id" to pass the host user's UID directly into the container, or ensure the image's process runs as a UID that maps correctly. Test volume write permissions explicitly.
userns_mode: "keep-id"
Ignored / Unsupported
Allowed
Use carefully and guard with overrides. Podman uses this to pass the host user's UID into a rootless container to bypass volume write permission issues. Docker does not recognise this key and may throw an unsupported key warning or error depending on version. Use a Podman-specific compose override file rather than placing this in the shared compose file.
Service Lifecycle & Dependencies
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
depends_on (start order only)
Allowed
Allowed
Supported for startup ordering across both engines. However, depends_on without a condition only checks that the container has started — not that the service inside is ready. On resource-constrained edge devices this causes race conditions.
depends_on: condition: service_healthy
Allowed
Allowed
Use with healthcheck: defined. This is the correct pattern for dependent services. Without a healthcheck: block on the dependency, the condition cannot be evaluated and the behaviour falls back to start-order only. Always pair condition: service_healthy with an explicit healthcheck: definition.
healthcheck:
Allowed
Allowed
Define explicitly for all services that other services depend on. Without a healthcheck, there is no reliable way to know when a service is ready to accept connections. This is especially important on edge devices with constrained CPU and memory where startup times are variable.
restart: policy
Allowed
Conditional
Podman honours restart policies (e.g., always, unless-stopped) for crash recovery. However, restart-on-reboot requires systemd integration — either systemd user linger enabled or Podman Quadlet unit files. The policy alone does not survive a host reboot on Podman without this. Docker with its daemon handles reboot restart natively.
Resource Management
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
deploy.resources.limits: (cpus, memory)
Allowed
Allowed
Use this form for portable resource limits. Both Docker Compose v2 and Podman support resource limits expressed under deploy.resources.limits as per the Compose Specification. This is the correct portable form for edge devices where workload isolation and resource governance matter.
Legacy top-level mem_limit: / cpus:
Allowed (legacy)
Varies
Do not use. These are legacy Compose v1 keys. Docker Compose v2 may accept them with a warning; podman-compose support is inconsistent. Use deploy.resources.limits instead.
Build
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
build: with dockerfile_inline
Allowed
Fails
Do not use.dockerfile_inline is a Compose Specification feature that podman-compose does not implement. The failure is in the translation layer — Buildah (Podman's build engine) can accept Dockerfile content from stdin, but podman-compose does not pass it through. Fix: Use a standard standalone Dockerfile file.
extra_hosts: in build: block
Allowed
Fails in translation
Do not use extra_hosts inside the build: block.podman-compose does not correctly pass this through to the build context. Note: Buildah itself supports --add-host natively — the limitation is in podman-compose's translation, not in Podman's build engine. Move extra_hosts to the service level if needed at runtime.
.dockerignore file
Allowed
Respected by Buildah
Do not assume it is ignored by Podman. Podman's build engine (Buildah) respects .dockerignore during podman build. The limitation is specific to how some versions of podman-compose pass build context. Do not rename to .pignore — this is not a recognised Podman convention and will break Docker builds.
Configuration & Secrets
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
secrets: / configs:
Allowed
Partial
Use with caution. Podman has a native secret model (podman secret) that differs architecturally from Docker's. Simple file-based secrets work across both. Advanced external source types and complex path configurations are not reliably translated by podman-compose. For production use, prefer Podman-native secret management or mount secrets as files via volumes.
.env file — variable interpolation
Allowed
Allowed
Supported on both. However, Docker Compose and podman-compose handle .env file precedence differently when multiple env files are specified or when shell environment variables are present. Shell variables take precedence over .env in Docker; verify this behaviour holds on your target podman-compose version.
.env file — comment formatting
Allowed
Version-dependent
Format carefully for older deployments. Docker skips blank lines and # comments anywhere in a .env file. Older versions of podman-compose had a bug where a # comment on the first line caused parse failures — fixed in recent versions. If targeting older deployments, start .env files with a key-value pair, not a comment.
env_file: directive
Allowed
Allowed
Supported on both, but resolution of relative paths and precedence when multiple env_file: entries are specified can differ between docker compose and podman-compose. Test explicitly if using multiple env files.
Logging
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
logging: driver: json-file
Allowed
May differ
Do not hardcode the logging driver unless required. Docker defaults to json-file. Podman on many Linux distributions defaults to journald. A compose file that hardcodes driver: json-file will behave differently on a Podman host using journald. Recommendation: omit the logging: block and accept engine defaults, or test the target host's logging driver explicitly and use environment-specific overrides.
Compose File Structure
Compose Property / Feature
Docker Compose Status
Podman Status
Impact & Action Required for Dual Portability
include: (top-level)
Allowed
Fails
Do not use. The include directive splits configuration across multiple files. podman-compose does not properly resolve nested paths or merges. Keep all service definitions inside a single unified file.
deploy: (Swarm mode keys)
Allowed (Swarm only)
Ignored
Do not use Swarm-specific deploy: keys.podman-compose silently ignores the deploy: block — it does not fail loudly. Note: docker compose v2 also ignores most deploy: keys outside of Swarm mode. Exception: deploy.resources.limits is part of the Compose Specification and is supported on both — use this for resource limits.
profiles:
Allowed
Partial
Minimise use. Docker natively supports optional container groups via profiles. podman-compose support varies by version — older versions may not honour the profile flag, with behaviour ranging from silently ignoring profiles to throwing an error. Test against your target deployment's actual podman-compose version before relying on profiles.
cap_add: - NET_RAW
Allowed
Required for ICMP
Explicitly add if your application uses ping or raw sockets. Docker permits ICMP by default. Rootless Podman drops non-root network packets unless NET_RAW is explicitly added under cap_add.
Checklist for Developers
FQIN for all images: Every image declaration must begin with a registry domain (docker.io/..., quay.io/..., ghcr.io/...). Never rely on short-name resolution — it is ambiguous on Podman and non-deterministic in CI/CD pipelines.
Digest-pin images for production: Use image: docker.io/library/mysql@sha256:... for edge deployments where reproducibility and offline determinism are required. Tags are mutable.
Named volumes for persistent data: Use named volumes for all persistent application state. Avoid host bind mounts unless you explicitly need host path access — and add :Z/:z flags when you do.
Explicit networks: Define user-named bridge networks explicitly. Do not rely on default network behaviour — DNS resolution semantics differ between engines.
Resource limits via deploy.resources.limits: Express CPU and memory limits using the Compose Specification form, not legacy top-level keys. Both Docker Compose v2 and Podman support this form.
Healthchecks for all depended-upon services: Define healthcheck: on every service that others depend on. Use depends_on: condition: service_healthy for reliable startup ordering on resource-constrained devices.
Set pull_policy: if_not_present: Prevents unnecessary pull attempts on devices with intermittent connectivity and makes offline behaviour deterministic.
No container_name: Let the engine generate container names. Hardcoded names break scaling and cause naming collisions.
Single unified file: Do not use include: or multi-file compose chains. Keep all definitions in one file.
No Swarm deploy: keys: Remove all Swarm-specific configuration. Use deploy.resources.limits only.
Test logging driver: Do not hardcode logging: driver:. Verify the target host's default logging driver and use overrides if needed.
Test on both runtimes locally: Run with docker compose up first, then with podman-compose up in rootless mode. Check for SELinux denials with ausearch -m avc -ts recent. Verify image pulls succeed without interactive prompts in non-interactive mode.
Check podman-compose version on target hosts: Features like profiles:, secrets:, and env_file: precedence behaviour vary significantly across podman-compose versions. Run podman-compose --version on the target device before relying on these features.
Minimal Portable Compose File Example
# compose.yaml — portable across Docker Compose and Podman# All images use FQIN. Named volumes. Explicit network. No container_name.services:
db:
image: docker.io/library/postgres:15.4 # FQIN — no short namespull_policy: if_not_present # deterministic on edge devicesenvironment:
POSTGRES_PASSWORD: examplevolumes:
- db-data:/var/lib/postgresql/data # named volume — not a bind mountnetworks:
- backendhealthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]interval: 10stimeout: 5sretries: 5restart: unless-stoppeddeploy:
resources:
limits:
cpus: "1.0"memory: 512Mapp:
image: docker.io/myorg/myapp:1.2.3pull_policy: if_not_presentports:
- "8080:8080"# always quotednetworks:
- backenddepends_on:
db:
condition: service_healthy # waits for healthcheck, not just startrestart: unless-stoppeddeploy:
resources:
limits:
cpus: "0.5"memory: 256Mvolumes:
db-data: # named volume — engine-managednetworks:
backend:
driver: bridge # explicit user-defined bridge
Activity
[-]Define improvements to compose defined workloads when targeting typical targets[/-][+]Define improvements to compose manifest workloads when targeting typical targets[/+]ajcraig commentedon Mar 19, 2026
Something related to this feature includes this issue found at plugfest:
Compose workloads should use named volumes for persistent data.
Related to this issue from Plugfest.
[-]Define improvements to compose manifest workloads when targeting typical targets[/-][+]Define improvements to compose manifest workloads when targeting typical targets(Docker/Podman)[/+]nilanjan-samajdar commentedon Jun 15, 2026
This is a corollary feature to have support for a common docker-compose yaml file suitable for Podman and Docker Compose.
However, we first need to add support for Podman as a runtime in Margo.
Currently we support only Docker Compose and Kubernetes as runtimes.
nilanjan-samajdar commentedon Jun 29, 2026
@singhmj-1
Can you analyze what it takes to add Podman support in the Device Client ?
singhmj-1 commentedon Jul 8, 2026
Cross-Runtime Feature Matrix
Use this reference table to design or refactor your
compose.yaml(ordocker-compose.yml) workloads for dual portability across Docker Compose and Podman (includingpodman-compose).Image & Registry
image: short-name(e.g.,mysql:latest)docker.io/library/mysql:latest. FQIN is a universal portability requirement, not a Podman-specific workaround.pull_policy:pull_policy: if_not_presentto prevent unnecessary pull attempts on devices with intermittent connectivity. Without this, behaviour differs between runtimes and versions — Docker uses cached images by default, Podman's behaviour depends on the--pullflag. Explicit policy makes offline behaviour deterministic on both.Container Identity & Naming
container_name--scalefunctionality and can clash with pod naming conventions when usingpodman-composewith pod arguments. Let the engine generate names automatically. Neither runtime requires explicit container names for inter-service communication — use service names instead.${COMPOSE_PROJECT_NAME}incontainer_namecontainer_name. Docker evaluates project variables dynamically.podman-composedoes not reliably support string interpolation for all fields, particularlycontainer_name. Use static values.Ports & Networking
ports: - "8080:80"network_mode: "host"net.ipv4.ip_unprivileged_port_start. Plan port assignments accordingly.networks:(Bridge)podman-compose.ipc: hostipc: hostshares only the IPC namespace of the non-root user who started the engine — not the entire host IPC namespace. Verify your application's IPC requirements against this constraint.Volumes & Storage
volumes:(Named) — persistent datavolumes:(Host Bind Mount):Z(private, unshared) or:z(shared) flags — e.g.,./data:/app/data:Z— to correctly remap SELinux labels and permissions. Without these flags, the container may be denied read/write access on SELinux-enforcing hosts.userns_mode: "keep-id"to pass the host user's UID directly into the container, or ensure the image's process runs as a UID that maps correctly. Test volume write permissions explicitly.userns_mode: "keep-id"Service Lifecycle & Dependencies
depends_on(start order only)depends_onwithout a condition only checks that the container has started — not that the service inside is ready. On resource-constrained edge devices this causes race conditions.depends_on: condition: service_healthyhealthcheck:defined. This is the correct pattern for dependent services. Without ahealthcheck:block on the dependency, the condition cannot be evaluated and the behaviour falls back to start-order only. Always paircondition: service_healthywith an explicithealthcheck:definition.healthcheck:restart:policyalways,unless-stopped) for crash recovery. However, restart-on-reboot requires systemd integration — either systemd user linger enabled or Podman Quadlet unit files. The policy alone does not survive a host reboot on Podman without this. Docker with its daemon handles reboot restart natively.Resource Management
deploy.resources.limits:(cpus,memory)deploy.resources.limitsas per the Compose Specification. This is the correct portable form for edge devices where workload isolation and resource governance matter.mem_limit:/cpus:podman-composesupport is inconsistent. Usedeploy.resources.limitsinstead.Build
build:withdockerfile_inlinedockerfile_inlineis a Compose Specification feature thatpodman-composedoes not implement. The failure is in the translation layer — Buildah (Podman's build engine) can accept Dockerfile content from stdin, butpodman-composedoes not pass it through. Fix: Use a standard standaloneDockerfilefile.extra_hosts:inbuild:blockextra_hostsinside thebuild:block.podman-composedoes not correctly pass this through to the build context. Note: Buildah itself supports--add-hostnatively — the limitation is inpodman-compose's translation, not in Podman's build engine. Moveextra_hoststo the service level if needed at runtime..dockerignorefile.dockerignoreduringpodman build. The limitation is specific to how some versions ofpodman-composepass build context. Do not rename to.pignore— this is not a recognised Podman convention and will break Docker builds.Configuration & Secrets
secrets:/configs:podman secret) that differs architecturally from Docker's. Simple file-based secrets work across both. Advanced external source types and complex path configurations are not reliably translated bypodman-compose. For production use, prefer Podman-native secret management or mount secrets as files via volumes..envfile — variable interpolationpodman-composehandle.envfile precedence differently when multiple env files are specified or when shell environment variables are present. Shell variables take precedence over.envin Docker; verify this behaviour holds on your targetpodman-composeversion..envfile — comment formatting#comments anywhere in a.envfile. Older versions ofpodman-composehad a bug where a#comment on the first line caused parse failures — fixed in recent versions. If targeting older deployments, start.envfiles with a key-value pair, not a comment.env_file:directiveenv_file:entries are specified can differ betweendocker composeandpodman-compose. Test explicitly if using multiple env files.Logging
logging: driver: json-filejson-file. Podman on many Linux distributions defaults tojournald. A compose file that hardcodesdriver: json-filewill behave differently on a Podman host using journald. Recommendation: omit thelogging:block and accept engine defaults, or test the target host's logging driver explicitly and use environment-specific overrides.Compose File Structure
include:(top-level)includedirective splits configuration across multiple files.podman-composedoes not properly resolve nested paths or merges. Keep all service definitions inside a single unified file.deploy:(Swarm mode keys)deploy:keys.podman-composesilently ignores thedeploy:block — it does not fail loudly. Note:docker composev2 also ignores mostdeploy:keys outside of Swarm mode. Exception:deploy.resources.limitsis part of the Compose Specification and is supported on both — use this for resource limits.profiles:podman-composesupport varies by version — older versions may not honour the profile flag, with behaviour ranging from silently ignoring profiles to throwing an error. Test against your target deployment's actualpodman-composeversion before relying on profiles.cap_add: - NET_RAWNET_RAWis explicitly added undercap_add.Checklist for Developers
docker.io/...,quay.io/...,ghcr.io/...). Never rely on short-name resolution — it is ambiguous on Podman and non-deterministic in CI/CD pipelines.image: docker.io/library/mysql@sha256:...for edge deployments where reproducibility and offline determinism are required. Tags are mutable.:Z/:zflags when you do.deploy.resources.limits: Express CPU and memory limits using the Compose Specification form, not legacy top-level keys. Both Docker Compose v2 and Podman support this form.healthcheck:on every service that others depend on. Usedepends_on: condition: service_healthyfor reliable startup ordering on resource-constrained devices.pull_policy: if_not_present: Prevents unnecessary pull attempts on devices with intermittent connectivity and makes offline behaviour deterministic.container_name: Let the engine generate container names. Hardcoded names break scaling and cause naming collisions.include:or multi-file compose chains. Keep all definitions in one file.deploy:keys: Remove all Swarm-specific configuration. Usedeploy.resources.limitsonly.logging: driver:. Verify the target host's default logging driver and use overrides if needed.docker compose upfirst, then withpodman-compose upin rootless mode. Check for SELinux denials withausearch -m avc -ts recent. Verify image pulls succeed without interactive prompts in non-interactive mode.podman-composeversion on target hosts: Features likeprofiles:,secrets:, andenv_file:precedence behaviour vary significantly acrosspodman-composeversions. Runpodman-compose --versionon the target device before relying on these features.Minimal Portable Compose File Example
feat(compose): implement SUP-01 Compose OCI — The Margo Way
feat(compose): implement SUP-01 Compose OCI — The Margo Way
feat(compose): implement SUP-01 Compose OCI — The Margo Way
feat(compose): implement SUP-01 Compose OCI — The Margo Way