# Docker Swarm **Last updated:** 2026-08-11 ## Overview Container orchestration uses Docker Swarm managed through Portainer. All production services are deployed as Swarm stacks, not standalone containers. ## Topology | Role | Hostname | IP | |------|----------|----| | Swarm manager | impulse-controller | 192.168.50.120 | | Database node | memory-archives | 192.168.50.107 | | Hypervisor | *(Xen Orchestra)* | 192.168.50.100 | - **Portainer:** https://192.168.50.120:9443 - **Hypervisor:** Xen (Xen Orchestra UI at 192.168.50.100) ## Databases All databases are centralised on `memory-archives` (192.168.50.107): | Engine | Notes | |--------|-------| | PostgreSQL 16 + pgvector | Primary relational store; vector extension enabled | | MySQL | Legacy / compatibility workloads | Services should connect to `memory-archives` by hostname, not IP, where Swarm DNS resolves within the overlay network. ## Stack YAML Conventions ```yaml # No top-level 'version:' field — Swarm ignores it and modern Compose drops it services: myservice: image: example/myservice:latest restart: unless-stopped # required on all services networks: - master-proxy-overlay deploy: labels: # Traefik labels go under deploy:, not at the service root - "traefik.enable=true" - "traefik.http.routers.myservice.rule=Host(`myservice.pldyn.net`)" - "traefik.http.routers.myservice.entrypoints=websecure" - "traefik.http.routers.myservice.tls.certresolver=cloudflare" # HTTP → HTTPS redirect - "traefik.http.routers.myservice-http.rule=Host(`myservice.pldyn.net`)" - "traefik.http.routers.myservice-http.entrypoints=web" - "traefik.http.routers.myservice-http.middlewares=redirect-to-https@docker" networks: master-proxy-overlay: external: true ``` Key conventions: - No `version:` field at the top of the file - `restart: unless-stopped` on every service - Traefik labels placed under `deploy:` (not at service root) - HTTP→HTTPS redirect middleware included for every public-facing service ## Service Discovery Swarm services are referenced by **service name**, not container name. Container names are non-deterministic across replicas; service names are stable within the overlay network. ## Sharp Edges ### Portainer env UI passes literal quotes When setting environment variables via the Portainer UI, the value field passes content verbatim — including any quotes you type. Do **not** wrap values in `"..."` or `'...'` in the UI; the quotes become part of the value and will break the application. **Correct:** `MY_VAR=somevalue` **Wrong:** `MY_VAR="somevalue"` ← the quotes are included in the string