Placement constraints

deploy.placement.constraints limits the nodes a service may run on: by role, host name, or labels you put on nodes. It is how a stateful service stays where its data is.

How it works

docker node update --label-add pgdata=true worker-1 labels a node; node.labels.pgdata == true, node.role == worker or node.hostname != manager-1 are constraints. If no node matches, the task stays Pending with no suitable node. placement.preferences spreads replicas over a label (zones, racks); max_replicas_per_node caps them per node.

What breaks

Volumes are local to a node. A database without a constraint is rescheduled on another node when its node fails, starts on a new empty volume, and the app writes into it as if nothing happened: the real data is still on the dead node. A constraint trades that silent loss for visible downtime.

In a terminal

docker node update --label-add pgdata=true worker-1

# stack.yaml
#   db:
#     deploy:
#       placement:
#         constraints:
#           - node.labels.pgdata == true

See it break, then fix it

Write the Dockerfile or the compose file, type the docker commands, and watch a simulated server run them through reboots, upgrades and traffic. Free to start.