mirror of
https://github.com/bestnite/quadlet-migrator-skill.git
synced 2026-04-04 01:23:28 +00:00
init
This commit is contained in:
177
references/compose-mapping.md
Normal file
177
references/compose-mapping.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user