The target's descriptor — /etc/cairn/adopt.toml¶
Where the manifest says what image to build, the descriptor says what this box
runs: which image and tag to watch, which frappe_docker overrides compose the stack,
the site name, and a reference to how secrets are wired. cairn-adopt reconcile reads
it to converge the running stack; its mere presence on a host is what marks that host as
a target at all.
It is host state, not deployment state — it describes this machine, not the
deployment — so it is never committed with the manifest, and reconcile never searches
for it: the path is fixed, because reconcile runs unattended under a timer with nobody
present to pass a flag.
You don't hand-write this file¶
Run cairn-adopt examine against the frappe_docker stack already running on the
host. It surveys the live deployment — compose project, layered overrides, site, the
image and tag currently deployed — and prints a descriptor; it writes nothing. You
review the output and install it yourself:
sudo cairn-adopt examine --environment production
# Generated by `cairn-adopt examine` from the deployment running on this host.
# Review it, then install it as /etc/cairn/adopt.toml.
environment = "production"
registry_host = "ghcr.io"
image = "acmecorp/erpnext-v16"
tag = "production"
site = "erp.acmecorp.com"
[compose]
directory = "/opt/frappe_docker"
# Read from the running project, not assumed — a hand-built deployment may not call it
# "compose.yaml".
file = "compose.yaml"
project = "acmecorp"
# Layered in this order — compose applies later files over earlier ones.
overrides = ["mariadb", "redis", "https"]
[health]
# timeout_seconds and interval_seconds default if omitted; url is deliberately
# left unset unless a real health-check endpoint exists.
Anything examine cannot determine is reported, with the reason, rather than filled
with a guess — a transcription error here is not a typo, it's a wrong deploy. What it
prints is round-tripped through cairn's own loader before being offered, so it can never
hand you something reconcile would go on to reject.
Fields¶
| Key | Required | Meaning |
|---|---|---|
environment |
yes | This host's environment name — joined to the manifest side only by the registry tag both agree on, never by this string directly. A label only: reconcile runs unattended under a timer with no interactive gate of any kind, so naming a target "production" has no functional effect here — the :production confirmation gate lives entirely on the build side, at the pointer move (cairn-build assign-tag/retire), not at convergence. |
registry_host |
yes | The registry this image lives in — kept separate from image rather than one string glued together, mirroring the manifest's own [cairn.registry] host. Required and explicit, including for Docker Hub: write "docker.io", the same canonical name docker itself normalizes a hostless reference to (registry.py's _DOCKER_HUB_NAMES) — there's no case where this value needs to be invented, only named. examine fills it in from the running container either way, "docker.io" included when the reference it finds has no host of its own. |
image |
yes | The image repository this host watches, without the registry host (that's registry_host, above). registry_host/image together are what reconcile actually pulls (repository), and repository:tag is the full watched reference. |
tag |
yes | The tag this host watches — the desired-state pointer. |
site |
yes | The one site this environment runs. Each environment is exactly one site; multi-site hosts are rejected — adopting one would silently drop every site but the first from the proxy configuration. |
[compose]¶
| Key | Required | Meaning |
|---|---|---|
overrides |
no | Ordered list of frappe_docker override names (mariadb, redis, https, …) — later entries layer over earlier ones, same order docker compose -f would take them. |
directory |
no | Where the frappe_docker compose tree lives on this host. |
file |
no | The base compose file's name within directory. Defaults to compose.yaml — frappe_docker's own convention — but examine always reads the real name off the running project and fills it in explicitly, since a hand-built deployment may call it anything. |
project |
no | Compose project name, so multiple stacks on one host can't collide. |
env_file |
no | An extra env file passed to compose, if you use one. |
[health]¶
| Key | Required | Meaning |
|---|---|---|
timeout_seconds |
no | Ceiling on convergence before a deploy is declared failed. Defaults to 600. |
interval_seconds |
no | Seconds between health probes. Defaults to 5; must not exceed timeout_seconds. |
url |
no | A URL reconcile fetches to confirm the site actually answers. Absent means container-health checks only — deliberately left unset by examine, since guessing a health endpoint wrong is worse than not checking one. |
[secrets]¶
| Key | Required | Meaning |
|---|---|---|
mechanism |
no | How DB/app secrets are wired: "docker-secrets" (recommended), "env-file", or "none" (default). Names the mechanism only — cairn never reads, stores, or sees a secret's actual value. |
Any key outside this set fails to parse, naming the bad key, rather than being silently ignored.