Deploy, Verify and Remove a Docker Stack
A Compose file that behaves on your laptop can still misfire the moment docker stack turns it into Swarm services. This walkthrough deploys a small stack, proves the services actually converged, updates it on purpose, then tears it down cleanly. Allow 15 to 30 minutes if a Swarm manager and reachable images already exist.
The route
Jump straight to the step you need, or tick off Done means at the end.
This is a manager-side workflow. Docker's stack command manages Swarm stacks; it is not the same as docker compose starting containers on one host. The examples use Docker CLI 29.8.1 from the installed docker-ce-cli package. For a multi-node Swarm, run the commands from a manager and use image names that every node can pull, normally from a registry.
Warning
This guide changes cluster state when you deploy and removes that state when you run the final command. Do not use the example stack name or file in a production Swarm without reviewing the images, ports, volumes and secrets first.
1. Confirm the CLI and Swarm context
Check the client version and whether the current Docker endpoint responds:
$ docker version --format '{{.Client.Version}}'
29.8.1
$ docker info --format 'Swarm: {{.Swarm.LocalNodeState}} ({{.Swarm.ControlAvailable}})'
Swarm: active (true)
The exact version and status will differ on your host. You need an active Swarm and a manager. If docker info reports an inactive Swarm, initialise or join one according to your cluster procedure before continuing; those actions change cluster membership and sit deliberately outside this guide. If the CLI needs elevated access on your system, use your normal Docker access arrangement rather than adding sudo to every command out of habit.
2. Create a minimal stack file
Make a working directory and write compose.yaml with an image your nodes can actually pull. This example publishes the web service through Swarm's routing mesh and keeps Redis internal to the stack:
$ mkdir -p ~/stackdemo
$ cd ~/stackdemo
$ editor compose.yaml
services:
web:
image: registry.example.invalid/example/web:1.0
ports:
- "8000:8000"
deploy:
replicas: 2
redis:
image: redis:7-alpine
- Replace the example image. A local image or a
buildinstruction is not a substitute for distributing the image to Swarm nodes. - Mind the file format. Stack deployment uses the legacy Compose file version 3 format, not every feature in the current Compose Specification, so check the supported stack syntax before copying a file meant only for local development.
Before touching the Swarm, ask Docker to render the merged and interpolated configuration:
$ docker stack config --compose-file compose.yaml
services:
redis:
image: redis:7-alpine
web:
deploy:
replicas: 2
image: registry.example.invalid/example/web:1.0
ports:
- mode: ingress
target: 8000
published: 8000
networks:
default: {}
Review this output for unintended published ports, image tags, environment interpolation and mounts. With multiple files, pass them all with repeated --compose-file options in the order you want them merged.
3. Deploy the stack
Warning
The next command creates or updates Swarm services and networks. Confirm the stack name and file are correct first.
$ docker stack deploy --compose-file compose.yaml stackdemo
Creating network stackdemo_default
Creating service stackdemo_web
Creating service stackdemo_redis
The command's default detached mode returns as soon as the desired state is submitted, not once every replica is ready. Add --with-registry-auth only when your deployment policy allows registry credentials to reach Swarm agents, and treat that option as security-sensitive: avoid exposing its output in shared logs.
Checkpoint
List the stack and confirm Docker sees it.
$ docker stack ls
NAME SERVICES
stackdemo 2
4. Verify services and tasks
Check the desired and running replica counts first:
$ docker stack services stackdemo
ID NAME MODE REPLICAS IMAGE
abc123 stackdemo_redis replicated 1/1 redis:7-alpine
def456 stackdemo_web replicated 2/2 registry.example.invalid/example/web:1.0
A count such as 1/2 means the desired state has not converged yet. Wait and repeat the command if a service is still pulling an image; appearing in the table is not the same as being ready. For the reason behind a missing replica, inspect the tasks:
$ docker stack ps stackdemo
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
abc111 stackdemo_web.1 registry.example.invalid/example/web:1.0 worker-1 Running Running 2 minutes ago
abc222 stackdemo_web.2 registry.example.invalid/example/web:1.0 worker-2 Running Running 2 minutes ago
abc333 stackdemo_redis.1 redis:7-alpine worker-1 Running Running 2 minutes ago
- Read the ERROR column. A rejected or repeatedly restarted task points there first.
- Check the usual suspects. An image workers cannot pull, an occupied published port, unsatisfied placement constraints, invalid mounts, or a process that exits.
- Use
--no-trunc. Add it when the normal task message is too short to be useful.
5. Update without losing your reference point
Change one intentional value in compose.yaml, such as the web image tag, render the file again, then redeploy the same stack name:
$ docker stack config --compose-file compose.yaml > rendered.yaml
$ less rendered.yaml
$ docker stack deploy --compose-file compose.yaml stackdemo
Updating service stackdemo_web
$ docker stack services stackdemo
The rendered file is a review aid, not a substitute for keeping the source file. Do not reach for --prune casually: it removes services belonging to this stack that are no longer referenced by the submitted file. Useful for deliberate cleanup, dangerous if someone expected an unreferenced service to stay.
Recovery
If an update turns out unhealthy, inspect docker stack ps stackdemo and the service logs with your normal tooling, then roll back using the previously reviewed image or Compose file and deploy it again. Keep the old file until the new tasks pass your application-level checks; Docker gives no universal health guarantee just because replicas read 2/2.
6. Remove the test stack
Destructive action
This removes the stack's services and networks, can interrupt users and discards attached resources, so verify the name before running it.
$ docker stack ls
$ docker stack rm stackdemo
Removing service stackdemo_web
Removing service stackdemo_redis
Removing network stackdemo_default
Run docker stack ls again to confirm the stack is gone. Removing a stack does not restore application data already written to external systems and is not a backup or rollback mechanism. Preserve named volumes, external networks or data services according to their own recovery procedure.
Done means
- The endpoint is confirmed. The current Docker endpoint is an active Swarm manager.
- The file was reviewed. The stack file was rendered and checked before deployment.
- Services look right.
docker stack servicesshows the expected service names and running replica counts. - Tasks look right.
docker stack psshows tasks running on the intended nodes without errors. - Updates were deliberate. They were reviewed before redeployment, and
--prunewas used only on purpose. - Cleanup was checked. The test stack was removed only after its name and operational impact were confirmed.