--- status: located opened: 2026-09-01 located-in: [mesh-control] fixed-by: amended-design: --- # 026 — The data directories are mounted and never declared ## Symptom Four modules mount **fourteen host paths** that no resource in those modules declares: ``` gitea /services/gitea/gitea postgres /services/postgres/db-data minio /services/minio/data/data1-1 mailu eleven more, including the mail spool and the admin database ``` Each is a bind mount on a container. None is a `directory` resource. The mesh has never heard of any of them. ## What that costs **They are created by the container runtime, as root.** A bind mount whose source does not exist is created for you, owned by root, with whatever mode the runtime picks. So `owner` and `mode` — which exist precisely so a module can say who its data belongs to — are silently not applied to the only directories that hold data. **The protection that exists for exactly this does not reach them.** A directory the mesh declared and no longer wants is *kept*, not removed, when it holds anything the mesh did not put there ([ADR 0030](../../02-DECISIONS/0030-data-outlives-the-mesh-that-declared-it.md)). That rule is the answer to *what happens to my data when a module goes away*, and it is written in terms of declared directories. **An undeclared one is not protected by it, because the mesh does not know it is there.** So the single rule guarding against data loss covers the configuration directories, which are cheap to lose, and not the data directories, which are the reason the rule exists. ## Where it came from These manifests were written by reading the arrangement being replaced and carrying its `docker-compose` files across — service, image, ports, volumes, environment — into the new manifest's container shape. That shape can express all of it, which is what made the transliteration feel like progress. **A container shape that can express a compose file will be filled in like a compose file.** The mesh's model is larger than that: a directory is a thing the mesh owns, with an owner and a mode and a rule about what happens when it is no longer wanted. A volume line borrowed from compose declares none of it, and nothing complains, because a bind mount source is a string. ## What a fix has to keep - **Every host path a container mounts is declared.** If a module wants a directory on the machine, it says so, with who owns it and what mode — and gets the removal rule with it. - **The check is mechanical.** A person comparing volumes against declared directories by hand is the process that produced this. It is a few lines against the manifest and belongs beside the other manifest checks. - **Not by inventing directories at apply time.** The host creating what a mount needs would make the mesh's ownership of a directory depend on which resource mentioned it first, and would put the same undeclared path back a layer down. ## Not yet answered **Where a module's data should live at all.** These paths were inherited whole from the arrangement being replaced, which put everything under one directory per service. Whether that is right here is a separate question, and a bigger one — it decides what a person backs up, and what survives a module being removed.