1
0
mirror of https://github.com/bestnite/quadlet-migrator-skill.git synced 2026-04-04 00:13:28 +00:00
Files
quadlet-migrator-skill/references/compose-mapping.md
2026-04-03 20:09:42 +11:00

178 lines
6.5 KiB
Markdown

# 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.<name>.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.<name>.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.