Chapter 39Lesson 02~210 minutes

Docker Swarm Fundamentals, Services, Stacks, Secrets, Overlay Networks, Rolling Updates, and Legacy Estate Support: Guided Hands-On Workflow and Core Operations

Build and inspect a disposable Swarm safely: services, tasks, overlay networking, fake secrets, rolling updates, rollback, and stack deployment with bounded cleanup.

Hands-onRolling updateRollbackStack deployEvidence

Learning objectives

  • Create and dismantle a disposable single-node Swarm without touching production membership.
  • Deploy and inspect a replicated service, task history, overlay network, fake secret, and stack.
  • Perform a bounded rolling update and explicit rollback while preserving first-failure evidence.
  • Compare docker compose and docker stack deploy behavior from observed output.
  • Clean only chapter-labeled resources and leave the disposable Swarm deliberately.
Safety boundary. Run the executable mutation path only against a disposable Engine or throwaway Linux VM that you are authorized to reset. If docker info shows an existing swarm, production containers, or unknown workloads, stop and use the read-only/conceptual path. Never initialize or leave Swarm on an estate you did not create for this lab.

1. Preflight: prove the host and context before changing cluster state

docker version
docker context show
docker info --format 'Swarm={{.Swarm.LocalNodeState}} Containers={{.Containers}}'
docker ps --format 'table {{.ID}}	{{.Names}}	{{.Image}}' 

The intended disposable precondition is Swarm=inactive. Existing standalone containers are not automatically deleted by swarm init, but this course still requires an isolated lab Engine because swarm membership, ingress networking, and later cleanup are host-level state changes.

2. Initialize a single-node training swarm

docker swarm init

docker info --format '{{json .Swarm}}'
docker node ls

docker swarm init makes the current Engine the first manager and creates cluster state plus built-in swarm networking. Do not copy or publish join tokens. A single manager is intentionally suitable only for this lab; it has zero manager-failure tolerance.

3. Resolve two disposable image tags to immutable digests

docker pull alpine:3.21
docker pull alpine:3.22
IMAGE_A=$(docker image inspect alpine:3.21 --format '{{index .RepoDigests 0}}')
IMAGE_B=$(docker image inspect alpine:3.22 --format '{{index .RepoDigests 0}}')
printf 'A=%s
B=%s
' "$IMAGE_A" "$IMAGE_B"

The tags are convenient inputs; the recorded repository digests become update evidence. If either tag is unavailable on a future date, choose two current Alpine minor releases, record their digests, and use those exact values throughout the lab.

4. Create a labeled overlay and fake Swarm secret

docker network create --driver overlay --label academy.chapter=39 ch39_overlay
printf 'training-only-value
' > ch39-secret.txt
docker secret create --label academy.chapter=39 ch39_fake_secret ch39-secret.txt
rm -f ch39-secret.txt

docker network inspect ch39_overlay --format '{{json .Labels}}'
docker secret inspect ch39_fake_secret --format '{{json .Spec.Labels}}' 

The secret’s value is intentionally not printed. Its object identity, labels, and service grant are enough to prove control-plane state.

5. Create a replicated service from digest A

docker service create \
  --name ch39_app \
  --label academy.chapter=39 \
  --network ch39_overlay \
  --secret ch39_fake_secret \
  --replicas 2 \
  --restart-condition on-failure \
  --update-parallelism 1 \
  --update-delay 3s   "$IMAGE_A"   sh -c 'test -s /run/secrets/ch39_fake_secret && while true; do sleep 30; done'

docker service inspect ch39_app --pretty
docker service ps --no-trunc ch39_app

On one node both replicas land on the same Engine, so this tests service/task semantics—not high availability. The secret grant is part of the service spec; task containers receive the secret only while they run.

6. Inspect the service, tasks, and node-local containers separately

docker service inspect ch39_app --format 'ID={{.ID}} Version={{.Version.Index}} Image={{.Spec.TaskTemplate.ContainerSpec.Image}}'
docker service ps --no-trunc --format 'table {{.ID}}	{{.Node}}	{{.DesiredState}}	{{.CurrentState}}	{{.Error}}' ch39_app
docker ps --filter label=com.docker.swarm.service.name=ch39_app --format 'table {{.ID}}	{{.Names}}	{{.Image}}' 

Service ID/version belongs to the manager object. Task IDs belong to scheduler attempts. Container IDs belong to the node runtime. Keep all three identities in incident evidence.

7. Perform a successful serial update to digest B

docker service update   --image "$IMAGE_B"   --update-parallelism 1   --update-delay 3s   --update-failure-action pause   ch39_app

docker service ps --no-trunc ch39_app
docker service inspect ch39_app --format '{{json .UpdateStatus}}'
docker service inspect ch39_app --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}' 

Watch task generations change. The old tasks remain visible in history, which is why you should preserve output instead of relying only on the currently running containers.

8. Roll back deliberately

docker service rollback ch39_app
docker service ps --no-trunc ch39_app
docker service inspect ch39_app --format 'Image={{.Spec.TaskTemplate.ContainerSpec.Image}} Rollback={{json .UpdateStatus}}' 

Rollback restores the previous service specification. Verify the resulting image digest and task states; do not infer success merely from the command returning zero.

9. Deploy a small stack and observe the compatibility boundary

cat > ch39-stack.yml <<EOF
version: "3.8"
services:
  sleeper:
    image: ${IMAGE_A}
    command: ["sh", "-c", "while true; do sleep 30; done"]
    networks: [labnet]
    deploy:
      replicas: 1
      labels:
        academy.chapter: "39"
networks:
  labnet:
    driver: overlay
EOF

docker stack deploy -c ch39-stack.yml ch39demo
docker stack services ch39demo
docker stack ps --no-trunc ch39demo

This file intentionally uses the V3-era deployment subset understood by docker stack deploy. A modern local Compose feature such as develop.watch is not proof of Swarm stack compatibility.

10. Inspect only chapter-owned resources before cleanup

docker service ls --filter label=academy.chapter=39
docker network ls --filter label=academy.chapter=39
docker secret ls --filter label=academy.chapter=39
docker stack ls

Inventory first. Stack-generated objects have stack namespace labels; the explicitly created service/network/secret also carry the chapter label.

11. Bounded cleanup

docker stack rm ch39demo
docker service rm ch39_app
docker secret rm ch39_fake_secret
docker network rm ch39_overlay
rm -f ch39-stack.yml

docker service ls
docker network ls --filter label=academy.chapter=39
docker secret ls --filter label=academy.chapter=39

No prune command is used. Removal names exact disposable objects.

12. Leave Swarm only after proving it is the disposable single-manager lab

docker node ls
docker info --format '{{json .Swarm}}'
# Only if this is the lab swarm you created above and it has one manager:
docker swarm leave --force
docker info --format '{{.Swarm.LocalNodeState}}' 
Safety boundary. A last manager requires --force to leave. That is acceptable only for this explicitly disposable one-node lab. In a real estate, manager removal is a quorum operation: demote/remove nodes with a plan and never force-leave casually.

13. Small challenge: choose the layer before the command

A service shows two desired replicas, but one slot repeatedly creates failed tasks. Which layer do you inspect first: the node-local container list, the service/task scheduler state, the overlay network, or the registry? Start with docker service ps --no-trunc to preserve task errors and placement evidence, then follow the error to image, node, secret/config, mount, or network state. The challenge is to identify the failing layer before “fixing” anything.

Knowledge check

Why is docker service ps usually more informative than docker ps for a failed Swarm rollout?

What did the single-node lab prove about overlay networking?

Why remove the stack before the externally created secret?

When is docker swarm leave --force appropriate in this lesson?

After an update command succeeds, what must you still verify?

Next lesson

Next: Docker Swarm Fundamentals, Services, Stacks, Secrets, Overlay Networks, Rolling Updates, and Legacy Estate Support: Configuration, Design Choices, and Tradeoffs

Continue with the next lesson in the course sequence and carry forward the evidence-first Docker operating model.

Official references and version notes

Baseline checked:

2026-09-22. Course baseline: Docker Engine/CLI 29.8.1, Compose 5.5.1, Buildx 0.37.1, BuildKit 0.33.0. The executable labs record the learner’s actual installed component versions. Swarm mode remains built into current Docker Engine and current Docker documentation explicitly describes it as a production runtime option; this chapter uses “legacy estate” to mean an existing platform that must be operated or evaluated deliberately, not that Swarm mode is removed.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.