Skip to content

Define improvements to compose manifest workloads when targeting typical targets(Docker/Podman) #168

Open
2 of 2 issues completed

Description

@ajcraig

Feature description

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.
  • Explicit ports, networks, and volumes
    • Docker Compose already encourages explicit definitions.
    • Podman requires this more often due to rootless execution, SELinux, and different networking behavior.
    • Explicit configuration is portable Compose, not Podman‑specific Compose.

Activity

  1. added this to the PR2 milestone on Mar 18, 2026
  2. 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
  3. ajcraig commented on Mar 19, 2026

    @ajcraig
    ContributorAuthor

    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.

  4. 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
  5. modified the milestones: PR2, PR3 on Jun 15, 2026
  6. nilanjan-samajdar commented on Jun 15, 2026

    @nilanjan-samajdar

    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.

  7. nilanjan-samajdar commented on Jun 29, 2026

    @nilanjan-samajdar

    @singhmj-1
    Can you analyze what it takes to add Podman support in the Device Client ?

  8. singhmj-1 commented on Jul 8, 2026

    @singhmj-1
    Contributor

    Cross-Runtime Feature Matrix

    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 names
        pull_policy: if_not_present             # deterministic on edge devices
        environment:
          POSTGRES_PASSWORD: example
        volumes:
          - db-data:/var/lib/postgresql/data   # named volume — not a bind mount
        networks:
          - backend
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U postgres"]
          interval: 10s
          timeout: 5s
          retries: 5
        restart: unless-stopped
        deploy:
          resources:
            limits:
              cpus: "1.0"
              memory: 512M
    
      app:
        image: docker.io/myorg/myapp:1.2.3
        pull_policy: if_not_present
        ports:
          - "8080:8080"                        # always quoted
        networks:
          - backend
        depends_on:
          db:
            condition: service_healthy         # waits for healthcheck, not just start
        restart: unless-stopped
        deploy:
          resources:
            limits:
              cpus: "0.5"
              memory: 256M
    
    volumes:
      db-data:                                 # named volume — engine-managed
    
    networks:
      backend:
        driver: bridge                         # explicit user-defined bridge
  9. added 3 commits that reference this issue on Jul 26, 2026
    d3c2cda
    ffd01b0
    e3ae992
  10. added a commit that references this issue on Aug 21, 2026
    721cdbd
  11. modified the milestones: PR3, PR4 on Sep 16, 2026
  12. moved this from Specification Improvements :SUP to GA1 in TWG Feature Projecton Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions