Swarm: Stack and Secrets — A Complete Deployment
You've reached the end
The final article ties everything together. In Article 8 we used Compose to run a multi-container app on one machine. Now we take that same idea up to a Swarm cluster with docker stack deploy, add safe password management with secrets, then clean everything up.
Stack: a Compose file for the whole cluster
A stack is a group of services declared in one compose file and deployed onto the swarm with a single command. It uses the exact compose format from Article 8, just adding a deploy: block to declare swarm-specific things (replica count, update policy...).
stack.yaml:
services:
web:
image: nginx:alpine
ports:
- "9092:80"
deploy:
replicas: 3 # 3 replicas, distributed by swarm
app:
image: alpine
command: sh -c "cat /run/secrets/db_password && sleep 600"
secrets:
- db_password # mount the secret into this service
deploy:
replicas: 1
secrets:
db_password:
external: true # secret created outside the stack beforehand
The difference from the compose file in Article 8: the deploy: block (replica count, and optionally update_config, restart_policy, placement...). Note build: cannot be used with a stack — swarm needs an image already on a registry so every node can pull it, so you build and push the image first (Articles 4/9), then reference it with image:.
Secrets: don't leave passwords out in the open
Recall Article 9: don't bake passwords into the image or expose them via environment variables (env vars can be read via docker inspect, logs...). Swarm has a safer secret mechanism: the password is stored encrypted in the cluster's Raft log, and is mounted only into the services that need it, as a file in the /run/secrets/ directory.
Create a secret (read from stdin so it isn't stored raw in shell history):
echo "sieu-bi-mat-123" | docker secret create db_password -
docker secret ls
A service that declares it uses the secret (as in stack.yaml) sees it at /run/secrets/db_password. The app reads that file to get the password, instead of reading an environment variable.
Deploying the stack
docker stack deploy -c stack.yaml mystack
Creating network mystack_default
Creating service mystack_web
Creating service mystack_app
Swarm creates a dedicated overlay network for the stack (recall Article 12) and the services. View the stack:
docker stack services mystack
NAME REPLICAS
mystack_app 1/1
mystack_web 3/3
Verify the secret really is mounted into the app container:
docker exec $(docker ps --filter name=mystack_app -q) cat /run/secrets/db_password
sieu-bi-mat-123
And the web is reachable via the routing mesh (Article 12):
curl http://localhost:9092
HTTP 200
The stack management commands:
docker stack ls # the running stacks
docker stack services mystack # services in the stack
docker stack ps mystack # tasks of the stack (on which nodes)
docker stack rm mystack # remove the whole stack
Comparing
docker composeanddocker stack: same file format, butcomposeruns on one machine (it readsbuild:), whilestackdeploys onto a swarm cluster (it readsdeploy:, needs a ready-made image). The same file can serve both, each side reading the part it cares about.
🧹 Full cleanup and leaving the swarm
This is the final article, so let's clean up everything the series created.
Remove the stack and secret:
docker stack rm mystack
docker secret rm db_password
Leave the swarm (the last manager node needs --force):
docker swarm leave --force
Check that swarm mode is off:
docker info --format '{{.Swarm.LocalNodeState}}'
inactive
Clean up the remaining containers/images/networks/volumes from the whole series:
docker system prune -a # stopped containers, unused images, leftover networks
docker volume prune # orphaned volumes (careful: data loss)
docker system df # see how much disk is still used
Series wrap-up
Across 14 articles, you've gone from "what is a container" to orchestrating a multi-service app on a multi-machine cluster:
- Fundamentals & deep dive (Articles 0–2): containers solve the environment problem; the client/daemon/containerd/runc architecture; and the lowest layer — namespaces, cgroups, the union filesystem.
- Core practice (Articles 3–9): running and managing containers; images and layers; writing a Dockerfile and build cache; volumes for persistent data; networking and service discovery; Compose for multi-container apps; optimizing images with multi-stage.
- Docker Swarm (Articles 10–13): cluster architecture and Raft; service, scale, rolling update; overlay network and routing mesh; stack and secrets.
A few recurring principles worth remembering:
- A container is just an isolated Linux process — no magic, just namespaces + cgroups + union FS.
- Order your layers wisely — least-changing first, most-changing last, for fast builds and small images.
- Data you need to keep must live outside the container — use volumes.
- Declare the desired state and let the system converge — the core mindset of orchestration.
Where to go next
Swarm is a good starting point for orchestration thanks to its simplicity. When you need to go further:
- Kubernetes: the de facto standard for orchestration at large scale, with more features than Swarm (but far more complex). Understanding Swarm makes Kubernetes easier to learn because many concepts overlap (service, desired-state, rolling update).
- CI/CD with Docker: automatically build images and deploy on every code push.
- A private image registry and security scanning in the pipeline.
- Observability: collecting logs and metrics from containers at cluster scale.
Thanks for following the series to the end. You can now not only use Docker, but understand how it works underneath — that's the difference between typing commands from memory and truly mastering the tool.
You've reached the end