mirror of
https://github.com/bestnite/quadlet-migrator-skill.git
synced 2026-04-04 00:13:28 +00:00
178 lines
6.5 KiB
Markdown
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.
|