3.7 KiB
3.7 KiB
| title | description | published | date | tags | editor | dateCreated |
|---|---|---|---|---|---|---|
| Docker Swarm Template Standard | Canonical YAML template and label rules for all Netgrimoire swarm services | true | 2026-04-12T00:00:00.000Z | keystone, docker, swarm | markdown | 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
# Deploy: docker stack deploy -c <service>.yaml <service>
services:
<servicename>:
image: <image>:latest
environment:
TZ: America/Chicago
volumes:
- /DockerVol/<servicename>:/config
# - /data/nfs/znas/Docker/<servicename>:/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: <servicename>.netgrimoire.com
caddy.reverse_proxy: <servicename>:<PORT>
caddy.import: crowdsec
caddy.import_1: authentik
# Uptime Kuma
kuma.<servicename>.http.name: <Service Name>
kuma.<servicename>.http.url: https://<servicename>.netgrimoire.com
# Homepage
homepage.group: <Group>
homepage.name: <Service Name>
homepage.icon: <service>.png
homepage.href: https://<servicename>.netgrimoire.com
homepage.description: <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/<service> |
Config, SQLite DBs, small app state. Only valid with a node.hostname placement constraint. |
/data/nfs/znas/Docker/<service> |
Bulk data, media, or any service without a hostname constraint |
Placement Constraints
Default (all services):
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):
constraints:
- node.hostname == docker4
- node.platform.arch != aarch64
- node.platform.arch != arm
Caddy Label Rules
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
# From services repo root
git add . && git commit -m "Add/update <service>" && git push
# On znas (or docker4 for Gremlin services)
cd ~/services && git pull
cd swarm/stack/<StackName>
set -a && source .env && set +a
docker stack config --compose-file <service>.yaml > resolved.yml
docker stack deploy --compose-file resolved.yml <service>
rm resolved.yml
docker stack services <service>