Stacks: deploying a Compose file
One file, a whole app, across nodes.
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.
services:web:image: registry.internal/web:1.4.2 # pre-built (stacks do not build)ports: ["80:3000"]networks: [appnet]deploy:replicas: 3update_config: { parallelism: 1, delay: 10s, failure_action: rollback }restart_policy: { condition: on-failure }resources:limits: { cpus: "0.50", memory: 256M }db:image: postgres:16networks: [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.
$ 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.
$ docker stack services paymentsID NAME MODE REPLICAS IMAGEa1b2c3 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