Docker Swarm

Home

Four-node Swarm cluster managed from PCT 108 (network, 192.168.2.82). PCT 109 is not a Swarm node.

Node Hostname IP Role
PCT 101 downloads 192.168.2.190 Worker
PCT 102 media-core 192.168.2.191 Worker
PCT 104 documents 192.168.2.105 Worker
PCT 107 debian 192.168.2.81 Worker
PCT 108 network 192.168.2.82 Manager

Key Commands

All Swarm commands run from the Proxmox host via pct exec 108 --.

# Status
pct exec 108 -- docker node ls
pct exec 108 -- docker stack ls
pct exec 108 -- docker service ls

# Inspect a service
pct exec 108 -- docker service ps <service>            # failed replica history
pct exec 108 -- docker service logs <service> --tail 50

# Deploy / update
pct exec 108 -- docker stack deploy -c <compose> <stack>
pct exec 108 -- docker service update --force <service>   # restart + fresh overlay IP

# Check containers on a specific node
pct exec 102 -- docker ps -a --filter name=<name>

Compose Files

Stored in Portainer on PCT 108:

/mnt/tank/appdata/portainer/compose/<id>/docker-compose.yml

Important: Portainer stores stack env vars in its internal DB (portainer.db) — there are no .env files on disk. When redeploying via CLI, export vars manually:

pct exec 108 -- bash -c 'export VAR=val && docker stack deploy -c <compose> <stack>'

Portainer Stack Directory → Stack Mapping

Dir ID Stack
2 documents-linkwarden
4 documents-paperless
6 network-guacamole
7 documents-nextcloud
10 documents-immich
22 documents-homarr
23 authentik
25 dozzel
26 media-core
28 documents-actual-budget
31 network-cloudbeaver
32 romm

Swarm Labels vs Container Labels

In Docker Swarm, only deploy.labels are read by Traefik — container-level labels: are ignored.

services:
  myservice:
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.myservice.rule=Host(`myservice.carr-family.org`)"

Overlay Network Health Check

Services on PCT 102 can develop stale VXLAN attachments after redeployments, causing 504s even though the service shows 1/1.

TRAEFIK=$(pct exec 108 -- docker ps -q --filter name=traefik)
IP=$(pct exec 102 -- docker inspect <container-name> --format '{{(index .NetworkSettings.Networks "traefik-public").IPAddress}}')
pct exec 108 -- docker exec $TRAEFIK ping -c 2 $IP
# Fix:
pct exec 108 -- docker service update --force <service-name>

Watchtower

Automatic image updates daily at 04:00 AM. See Network Stack for full config.

Excluding a container (required for network_mode: "service:..." deps):

labels:
  - "com.centurylinklabs.watchtower.enable=false"
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9