Stack Conventions¶
Machine translation of the French original.
Locations¶
| What | Where |
|---|---|
| Canonical Compose file | /opt/docker/<stack>/docker-compose.yml on the host |
| Stack documentation | 10-stacks/<stack>/README.md in the vault |
| Decision log | 10-stacks/<stack>/decisions.md in the vault |
| Encrypted secrets | 00-infra/secrets/<hôte>/<stack>.enc.env in the vault |
10-stacks/<stack>/compose.yml is a broken symbolic link
In the vault, this file points to /opt/docker/<stack>/docker-compose.yml — a path that only exists on the host. It is not read from vm-claude.
Useful consequence: modifying the file via SSH is sufficient, as the vault is already up-to-date since it only contains a link. There is nothing to synchronize.
The distinction that separates the two cases:
Service Skeleton¶
services:
monservice: # (1)!
image: monimage:1.4.2@sha256:... # (2)!
container_name: monservice
restart: on-failure:5 # (3)!
cpus: 0.5
mem_limit: 256m # (4)!
security_opt:
- no-new-privileges:true
networks:
- infrastructure_network # (5)!
labels:
- dockhand.update=false # (6)!
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:8080/healthz"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options:
max-size: 50m
max-file: "3" # (7)!
networks:
infrastructure_network:
external: true
- The service name is the one that Caddy will use as the hostname:
reverse_proxy monservice:8080. - Never
latest. A version tag and its digest: the tag indicates what we wanted, the digest indicates what we got. on-failure:5on vm-docker and vm-compute-01,alwayselsewhere — see Lifecycle.- Sizing rule: three times the observed idle consumption.
infrastructure_networkfor anything that Caddy needs to connect to;authentik_backend_networkplus if the service communicates with Authentik.- Dockhand updates images via opt-out. Without this label, a locally built image would be replaced with an empty container.
- Without these two lines, a verbose container fills the host's disk.
What a Compose file should never contain¶
- No clear secrets. Values are stored in
.envon the host, encrypted within the vault using SOPS + age. The names of the variables remain readable: agit diffindicates which secret has changed, but never its value. - No
.envread or copied into the vault. This is deliberate. - No publicly accessible ports that anyone needs. If Caddy is on the same network, it connects to the container by its name:
ports:only adds another surface.
The form for a required variable, which fails loudly rather than starting half-way:
Most importantly up -d¶
config interpolates the variables and validates the schema. A missing variable immediately appears, instead of producing a container that starts and serves no purpose.
Dernière vérification de cette page : 2026-08-29