--- title: Docker Swarm Template Standard description: Canonical YAML template and label rules for all Netgrimoire swarm services published: true date: 2026-04-12T00:00:00.000Z tags: keystone, docker, swarm editor: markdown dateCreated: 2026-04-12T00:00:00.000Z --- # Docker Swarm Template Standard All Swarm YAML files in `services/swarm/` and `services/swarm/stack/` must follow this standard. The Gremlin audit workflow checks compliance weekly. --- ## Canonical Template ```yaml # Deploy: docker stack deploy -c .yaml services: : image: :latest environment: TZ: America/Chicago volumes: - /DockerVol/:/config # - /data/nfs/znas/Docker/:/data networks: - netgrimoire deploy: restart_policy: condition: any delay: 5s max_attempts: 3 window: 120s placement: constraints: - node.hostname == znas - node.platform.arch != aarch64 - node.platform.arch != arm labels: # Caddy caddy: .netgrimoire.com caddy.reverse_proxy: : caddy.import: crowdsec caddy.import_1: authentik # Uptime Kuma kuma..http.name: kuma..http.url: https://.netgrimoire.com # Homepage homepage.group: homepage.name: homepage.icon: .png homepage.href: https://.netgrimoire.com homepage.description: # DIUN diun.enable: "true" networks: netgrimoire: external: true ``` --- ## Forbidden Fields Never use these at the service level: | Field | Reason | |-------|--------| | `version:` | Deprecated in Compose v2+ | | `container_name:` | Incompatible with Swarm replicas | | `restart:` | Use `deploy.restart_policy` instead | | `depends_on:` | Not supported in Swarm mode | | `endpoint_mode: dnsrr` | Breaks internal DNS — always use VIP | --- ## Volume Path Rules | Path | When to Use | |------|-------------| | `/DockerVol/` | Config, SQLite DBs, small app state. **Only valid with a `node.hostname` placement constraint.** | | `/data/nfs/znas/Docker/` | Bulk data, media, or any service without a hostname constraint | --- ## Placement Constraints **Default (all services):** ```yaml constraints: - node.hostname == znas - node.platform.arch != aarch64 - node.platform.arch != arm ``` ARM exclusion prevents accidental scheduling on Pi vault/worker nodes. Override only if the service is ARM-specific. For services pinned to docker4 (Gremlin stack): ```yaml constraints: - node.hostname == docker4 - node.platform.arch != aarch64 - node.platform.arch != arm ``` --- ## Caddy Label Rules ```yaml caddy: servicename.netgrimoire.com # no https:// prefix caddy.reverse_proxy: servicename:PORT # container name:port, NOT {{upstreams PORT}} caddy.import: crowdsec # always both caddy.import_1: authentik # always both, no exceptions ``` Never use `{{upstreams PORT}}` — it breaks during `docker stack config` preprocessing. **Wasted-bandwidth services** use `wasted-bandwidth.net` domain and `caddy.import_1: authelia` instead of authentik. --- ## Deploy Workflow ```bash # From services repo root git add . && git commit -m "Add/update " && git push # On znas (or docker4 for Gremlin services) cd ~/services && git pull cd swarm/stack/ set -a && source .env && set +a docker stack config --compose-file .yaml > resolved.yml docker stack deploy --compose-file resolved.yml rm resolved.yml docker stack services ```