Skip to content

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:

ls -la 10-stacks/<stack>/compose.yml
# une flèche `->`  : lien symbolique, rien à faire
# pas de flèche    : vrai fichier, à synchroniser par scp

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
  1. The service name is the one that Caddy will use as the hostname: reverse_proxy monservice:8080.
  2. Never latest. A version tag and its digest: the tag indicates what we wanted, the digest indicates what we got.
  3. on-failure:5 on vm-docker and vm-compute-01, always elsewhere — see Lifecycle.
  4. Sizing rule: three times the observed idle consumption.
  5. infrastructure_network for anything that Caddy needs to connect to; authentik_backend_network plus if the service communicates with Authentik.
  6. Dockhand updates images via opt-out. Without this label, a locally built image would be replaced with an empty container.
  7. 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 .env on the host, encrypted within the vault using SOPS + age. The names of the variables remain readable: a git diff indicates which secret has changed, but never its value.
  • No .env read 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:

environment:
  - API_TOKEN=${API_TOKEN:?rendu SOPS absent - voir 00-infra/secrets/README.md}

Most importantly up -d

docker compose -f /opt/docker/<stack>/docker-compose.yml config

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