Deploying Stacks

Deploying a Stack from a Compose File

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:

swarm-stack.yaml: BookNest as a Swarm stackYAML
# 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: {} }
compose.yaml fails; the secret and the stack file succeedYAML
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"
Output
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_index

The 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.