CoursesDocker in depthOrchestration with Swarm

Stacks: deploying a Compose file

One file, a whole app, across nodes.

Intermediate12 min · lesson 18 of 30

A stack is a whole multi-service application deployed to a swarm from a single Compose file — the orchestration equivalent of docker compose up, but across the cluster. docker stack deploy reads the file and creates every service, network, secret, and config in it as swarm objects. Re-running it against a changed file updates the stack in place, so the same file is both your deploy and your update.

stack.yaml
services:
web:
image: registry.internal/web:1.4.2 # pre-built (stacks do not build)
ports: ["80:3000"]
networks: [appnet]
deploy:
replicas: 3
update_config: { parallelism: 1, delay: 10s, failure_action: rollback }
restart_policy: { condition: on-failure }
resources:
limits: { cpus: "0.50", memory: 256M }
db:
image: postgres:16
networks: [appnet]
volumes: ["pgdata:/var/lib/postgresql/data"]
deploy:
placement: { constraints: ["node.labels.disk == ssd"] }
networks: { appnet: { driver: overlay } }
volumes: { pgdata: {} }

The deploy key

The deploy section is what makes a Compose file swarm-aware — plain Compose ignores it, the swarm reads it. It carries the orchestration policy: replica count, rolling-update behaviour, restart policy, per-task resource limits and reservations, and placement rules. This is exactly where single-host Compose becomes cluster deployment, so it is worth knowing every sub-key.

Scenario: pin the database to SSD nodes

Only some nodes have fast disks, and the database must land on them. Label those nodes, then add a placement constraint so the scheduler only considers matching nodes — the same mechanism pins workloads to a region, an availability zone, or manager-vs-worker roles. Constraints are how you turn a uniform-looking cluster into one with purpose-built pools.

terminal
$ docker node update --label-add disk=ssd node-2
$ docker node update --label-add disk=ssd node-3
# the stack’s db service has: placement.constraints: ["node.labels.disk == ssd"]
$ docker stack deploy -c stack.yaml payments
$ docker stack ps payments --filter name=payments_db --format '{{.Node}}'
node-2 # scheduled only where the label matches

Scenario: update and inspect a running stack

To ship a change you edit the file and re-deploy the same stack name; Swarm diffs it and rolls out only what changed, per each service’s update_config. The stack sub-commands are your window into it — list stacks, list their services and tasks, and tear the whole thing down in one command when you are done.

terminal
$ docker stack services payments
ID NAME MODE REPLICAS IMAGE
a1b2c3 payments_web replicated 3/3 registry.internal/web:1.4.2
$ docker stack ps payments # every task, node, and state
$ docker stack rm payments # remove the whole stack at once
Swarm stacks do not build images
docker stack deploy ignores the build: key — the swarm can only run images, not build them. Every service must reference a pre-built image in a registry the nodes can pull. So the workflow is build and push first, then deploy; a stack that works with compose up but fails in the swarm is almost always relying on build:.