docker stack deploy turns a Compose 514 file into services prefixed with the stack's name, plus networks, secrets and volumes. It honors deploy, but never builds images, ignores depends_on ordering, and follows the older Compose file format 3, so BookNest's compose.yaml fails at once (first line of the output below). A smaller file states what the cluster needs: registry images, replicas, an update policy, a placement rule that keeps PostgreSQL 1,289 and its volume on the manager, and the password as a Swarm 514 secret, stored encrypted in the Raft log and mounted only into the tasks that name it:
# Deploy: REGISTRY=<host:port> docker stack deploy -c swarm-stack.yaml booknest
services:
web:
image: ${REGISTRY:?set REGISTRY}/booknest-web:1.3
ports: ["8080:80"]
deploy: { replicas: 2 }
api:
image: ${REGISTRY}/booknest-api:1.3
environment: { PGHOST: db, PGUSER: booknest, PGDATABASE: booknest,
PGPASSWORD_FILE: /run/secrets/db_password }
secrets: [db_password]
deploy:
replicas: 3
update_config: { parallelism: 1, delay: 5s, order: start-first,
failure_action: rollback }
resources: { limits: { cpus: "1.0", memory: 256M } }
db:
image: ${REGISTRY}/postgres:18
environment: { POSTGRES_USER: booknest, POSTGRES_DB: booknest,
POSTGRES_PASSWORD_FILE: /run/secrets/db_password }
secrets: [db_password]
volumes: [pgdata:/var/lib/postgresql]
deploy: { placement: { constraints: [node.role == manager] } }
secrets: { db_password: { external: true } }
volumes: { pgdata: {} }m1 stack deploy -c compose.yaml booknest 2>&1 | head -1
openssl rand -base64 18 | m1 secret create db_password - >/dev/null
m1 stack deploy -d -c swarm-stack.yaml booknest
until m1 service ls -f name=booknest_api | grep -q ' 3/3 '; do sleep 2; done
m1 stack services booknest --format '{{.Name}} {{.Replicas}} {{.Ports}}'
m1 service logs booknest_api 2>&1 | grep -oE 'ECONNREFUSED|ENOTFOUND|pg_type\w*' \
| sort | uniq -c
git add swarm-stack.yaml && git commit -qm "Add a Docker Swarm stack file"services.test.depends_on: must be a list
Creating network booknest_default
Creating service booknest_db
Creating service booknest_web
Creating service booknest_api
booknest_api 3/3
booknest_db 1/1
booknest_web 2/2 *:8080->80/tcp
10 ENOTFOUND
1 pg_type
2 pg_type_typname_nsp_indexThe stack came up, but not cleanly: API tasks started before the db service's name existed (ENOTFOUND; in other runs also before PostgreSQL listened, ECONNREFUSED), exited, and were restarted until they stayed up. In Swarm, as in Kubernetes 5,150 , services must tolerate absent dependencies, and the restart policy is the retry loop. The pg_type lines are a real race: two replicas ran BookNest's CREATE TABLE IF NOT EXISTS at the same moment, and PostgreSQL rejected one with a duplicate key in its catalog index pg_type_typname_nsp_index. Schema setup belongs in one migration step, not in every replica's startup; Kubernetes revisits this.