# Compose Mapping Use this file when converting `docker-compose.yml` or `compose.yaml` into Quadlet units. ## General approach - Model each service first, then decide how to group all resulting containers into one or more `.pod` units. - Prefer maintainable Quadlet output over mechanical one-to-one translation. - Keep names stable and predictable. Generated filenames should use a shared project prefix. ## Field strategy ### `name` - Use as an application prefix when it improves unit naming clarity. - Do not force a top-level project name into every filename if the user prefers shorter units. ### `services..image` - Map to `Image=` in `[Container]`. - Prefer fully qualified image names when normalizing output. - If the source omits a registry and is using Docker Hub semantics, normalize it explicitly for Podman. - Use these rules when filling in Docker Hub references: - `redis:7` -> `docker.io/library/redis:7` - `nginx` -> `docker.io/library/nginx` - `langgenius/dify-api:latest` -> `docker.io/langgenius/dify-api:latest` - Do not guess `docker.io/library/...` for images that already include a namespace. ### `services..build` - Prefer upstream published images over local builds when the project already documents supported registry images. - If the user wants declarative builds, create a `.build` unit and reference it from `Image=`. - If the build semantics are too custom, or if an equivalent upstream image is clearly available, keep this as a manual follow-up item instead of guessing. ### `container_name` - Drop it. - Do not generate `ContainerName=`. Let Podman and systemd derive the runtime name. ### `ports` - For a standalone service, map to `PublishPort=` on the `.container`. - For a pod-based topology, prefer `PublishPort=` on the `.pod` when the published ports belong to the pod boundary rather than one child container. ### `volumes` - Bind mounts become `Volume=HOST:CONTAINER[:OPTIONS]`. - Normalize relative host paths against the Compose file directory and emit absolute paths in the final Quadlet output. - Named volumes can remain referenced by name, but when the user wants explicit infrastructure-as-code, create matching `.volume` units. - Ask the user which volume mode they want when the source does not make the intended persistence model obvious. ### `networks` - If the source uses a default network only, you often do not need a `.network` unit. - If a custom network is intentional and needs to be managed declaratively, create a `.network` unit and reference it explicitly. - Containers in the same `.pod` can communicate over `127.0.0.1` / `localhost` because they share a network namespace. - Containers in different pods must not be treated as reachable via `127.0.0.1` / `localhost`. - When splitting services across multiple pods, use container networking and service-level addressing, or publish ports to the host boundary when that is the intended access pattern. ### `environment` - Small stable values can become one or more `Environment=` lines. - Sensitive values should be moved to `EnvironmentFile=` unless the user explicitly wants them inline. ### `env_file` - Prefer `EnvironmentFile=`. - If there are multiple env files, preserve order and explain precedence if the user asks. ### `.env` interpolation - Resolve only when you have the actual source values. - If variables are missing, surface a missing-variable list. - Never silently replace unknown values with blanks. ### `profiles` - Decide first which profiles are actually part of the desired deployment. - Do not try to preserve Compose profiles as a direct Quadlet concept. - Treat profiles as source selection inputs that decide which services become units. ### `depends_on` - Translate to `Requires=` and `After=` when that reflects intent. - State clearly that this controls startup ordering, not application readiness. ### `restart` - Map common cases to `[Service] Restart=`. - If the source policy has no direct systemd equivalent, choose the nearest safe mapping and explain it. ### `healthcheck` - Prefer dedicated Quadlet health fields such as `HealthCmd=`, `HealthInterval=`, `HealthTimeout=`, `HealthRetries=` when representable. - If the Compose healthcheck is only partially representable, preserve the command intent and call out missing knobs. ### `command` and `entrypoint` - `entrypoint` typically maps to `Entrypoint=`. - `command` typically maps to `Exec=`. ### `user` - Map to `User=` and `Group=` in `[Container]` only when it is a container runtime user mapping, not a systemd service user. - Do not use systemd `User=` to try to make a rootless Quadlet run as another login user. ### unsupported or risky fields Handle these conservatively and usually as migration notes: - `deploy` - `profiles` - `extends` - advanced Compose merge behavior - readiness semantics hidden behind `depends_on` ## Minimal examples ### Single service to standalone container Source intent: ```yaml services: web: image: nginx:latest ports: - "8080:80" ``` Reasonable result shape: ```ini [Container] Image=docker.io/library/nginx:latest PublishPort=8080:80 ``` Use this when the deployment is truly a simple single-service case and pod semantics add little value. ### Small multi-service app to one pod Source intent: ```yaml services: api: image: ghcr.io/example/api:1.0 depends_on: - db db: image: postgres:16 ``` Reasonable result shape: - one `.pod` for the application boundary - one container unit for `api` - one container unit for `db` - `api` may reach `db` over `127.0.0.1` / `localhost` because both containers share the pod network namespace - ordering hints for startup, while explicitly noting that `depends_on` does not guarantee readiness Use this when shared networking and a grouped lifecycle make the deployment easier to operate. ## Pod decision rule Choose the simplest topology that preserves the source deployment intent. Prefer a single `.pod` for multi-container applications when practical. If the project is a simple single-container deployment with no real need for pod semantics, a standalone `.container` is acceptable. If shared networking, shared published ports, or tightly coupled service lifecycle make pod semantics useful, prefer one or more `.pod` units. If one pod is not practical because of port conflicts or clearly incompatible groupings, split the result into a small number of pods rather than forcing an awkward topology. For large application stacks with optional services, ask the user to choose the desired service set before generating a minimized result.