Inspect Docker Stack Merges Before You Deploy
You will use docker stack config to turn one or more Compose files into the final configuration Docker would pass to a stack deployment. This is a read-only inspection step: it does not create a Swarm service, change a running stack or modify the input files. Allow about ten minutes for a small stack, plus time to investigate anything unexpected in the rendered YAML.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Confirm the command and create a safe test area
- 2. Render one Compose file
- 3. Merge an override file in order
- 4. Check missing and deliberately supplied variables
- 5. Preserve variables for a later deployment
- 6. Read from standard input when the files are generated
- 7. Stop before deployment if the output is wrong
The examples use Docker Community CLI 29.8.1 from the installed docker-ce-cli package. The local manual describes this command as outputting the final file after merges and interpolations. Option details can vary between CLI releases, so check the installed version before copying a script into a different host.
1. Confirm the command and create a safe test area
Run these checks as your ordinary user. The command does not need sudo, and it does not need a Docker daemon for configuration rendering:
$ docker version --format '{{.Client.Version}}'
29.8.1
$ docker stack config --help
Usage: docker stack config [OPTIONS]
Outputs the final config file, after doing merges and interpolations
Use a temporary directory or an existing project directory where you can identify each Compose file clearly:
$ workdir="$(mktemp -d)"
$ cd "$workdir"
$ printf 'temporary files: %s\n' "$workdir"
temporary files: /tmp/tmp.example
The directory name is different on every run. Keep it until you have checked the output, then remove it with rm -rf -- "$workdir" if it contains only these disposable test files. That removal is irreversible. Do not substitute a shared project path.
2. Render one Compose file
Create a minimal input that uses an environment variable. The quoted here-document keeps the ${IMAGE_TAG} text in the file instead of expanding it while the file is created:
$ cat > base.yml <<'YAML'
services:
web:
image: "nginx:${IMAGE_TAG:-latest}"
ports:
- "8080:80"
YAML
$ IMAGE_TAG=1.27 docker stack config --compose-file base.yml
services:
web:
image: nginx:1.27
ports:
- mode: ingress
target: 80
published: 8080
protocol: tcp
app_protocol: http
Your formatting can differ slightly between releases, but the key check is that the image is rendered as nginx:1.27 and the port mapping is represented in the final configuration. The command prints YAML to standard output and leaves base.yml unchanged.
3. Merge an override file in order
Pass each file with --compose-file in the order you want Docker to merge them. Later files provide the environment-specific changes:
$ cat > production.yml <<'YAML'
services:
web:
deploy:
replicas: 3
environment:
APP_MODE: production
YAML
$ IMAGE_TAG=1.27 docker stack config \
--compose-file base.yml \
--compose-file production.yml
services:
web:
image: nginx:1.27
ports:
- mode: ingress
target: 80
published: 8080
protocol: tcp
app_protocol: http
environment:
APP_MODE: production
deploy:
replicas: 3
Inspect the merged result rather than assuming that a file was ignored. A common distraction trap is swapping the files, or passing only the override and wondering why the base image or ports disappeared. Keep the exact command in a script or review note if the merge is part of a release process.
4. Check missing and deliberately supplied variables
Interpolation happens by default. With the :-latest expression above, an unset IMAGE_TAG uses latest:
$ env -u IMAGE_TAG docker stack config --compose-file base.yml | grep 'image:'
image: nginx:latest
That fallback may be convenient for a test and unsafe for a release. Set a specific tag in the command environment or, preferably, in the deployment's controlled environment. Do not use a mutable tag when you need a reproducible rollout.
For a strict check, use a required variable expression in a separate input:
$ cat > required.yml <<'YAML'
services:
web:
image: "nginx:${REQUIRED_TAG?set REQUIRED_TAG before rendering}"
YAML
$ env -u REQUIRED_TAG docker stack config --compose-file required.yml
invalid interpolation format for services.web.image: "required variable REQUIRED_TAG is missing a value: set REQUIRED_TAG before rendering"
$ REQUIRED_TAG=1.27 docker stack config --compose-file required.yml | grep 'image:'
image: nginx:1.27
The exact error prefix can vary. The useful result is a non-zero command when the required value is absent. Treat that failure as a checkpoint, not as a reason to add a silent default.
5. Preserve variables for a later deployment
--skip-interpolation suppresses interpolation while still producing the merged configuration. This matters when the output will be piped to another Docker command that must perform interpolation itself:
$ cat > runtime.yml <<'YAML'
services:
web:
image: "nginx:$${IMAGE_TAG}"
environment:
APP_MODE: "${APP_MODE:-production}"
YAML
$ env -u IMAGE_TAG docker stack config --compose-file runtime.yml --skip-interpolation
services:
web:
image: nginx:$${IMAGE_TAG}
environment:
APP_MODE: "${APP_MODE:-production}"
Use this mode deliberately. If you only want to see the values that will reach a deployment now, leave the option out and set the variables explicitly. If you pipe output onward, review the whole pipeline first. A value containing a dollar expression can be expanded twice, which can change a route, secret reference or command argument.
6. Read from standard input when the files are generated
The manual also accepts - as the Compose file path. This is useful for a controlled pipeline, but keep the source visible while debugging:
$ IMAGE_TAG=1.27 cat base.yml | docker stack config --compose-file -
services:
web:
image: nginx:latest
In this example, the variable assignment belongs to cat, not to docker stack config, so Docker does not receive IMAGE_TAG. Put the assignment on the Docker command instead:
$ cat base.yml | IMAGE_TAG=1.27 docker stack config --compose-file - | grep 'image:'
image: nginx:1.27
That shell-scope detail is easy to miss in a long pipeline. If the rendered value matters, print or test it before sending the result anywhere else.
7. Stop before deployment if the output is wrong
Review the rendered file for image names, published ports, environment values, bind paths, secrets, networks and replica counts. Pay particular attention to values supplied by the shell: they are configuration input, not harmless text. Do not pipe output to docker stack deploy until the merge and interpolation mode match your deployment plan.
This command is observational, so recovery is normally just correcting an input file or environment variable and running it again. If a later command has already changed a running stack, use the deployment's documented rollback or redeploy process. docker stack config itself has not created anything to roll back.
Done means
- You confirmed the installed Docker CLI version and command syntax.
- You rendered the intended base and override files in the intended order.
- You checked interpolation with an explicit image tag rather than trusting a fallback.
- You know when
--skip-interpolationis needed for a later deployment step. - You checked standard-input pipelines for shell variable-scope mistakes.
- You reviewed the final YAML before running any command that changes a Swarm stack.