Docker Stack CLI Utility
A command-line tool for advanced Docker Swarm stack deployments on plain Docker daemons. docker-stack extends vanilla docker stack deploy with generated secrets, templated configs, versioned stack state, safer rollbacks, and better day-to-day stack workflows.
Installation
Install or upgrade docker-stack with:
pip install docker-stack --upgrade --break-system-packages
Quick Start
Plain Docker Daemon
If you already have a Docker Swarm daemon or Docker context, you can use the advanced stack features directly against it.
Typical daemon-only workflow:
docker-stack deploy my-stack docker-compose.yml
docker-stack ls
docker-stack ls -n team-a
docker-stack ls -A
docker-stack versions my-stack
docker-stack cat my-stack
docker-stack checkout my-stack v2
docker-stack node ls
What this gives you on a raw Docker daemon:
- richer secret and config handling in Compose
- generated secrets without external scripts
- template expansion from env vars and files
- versioned stack config history
- stack version inspection and checkout
- raw daemon compatibility without extra infrastructure
GitHub Actions
1. Normal Docker daemon
Use this when the runner already has Docker access through the default Docker context or DOCKER_HOST.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v6
with:
python-version: '3.x'
- run: python3 -m pip install --upgrade docker-stack
- run: docker-stack deploy my-stack docker-compose.yml
Use this option when CI can connect directly to the target Docker daemon.
2. Docker-Manager
Use the bundled action when deploying through Docker-Manager. The manager repository contains the server source, installation instructions, and deployment documentation.
For a full compose deployment directly from CI, use the action to configure
Docker-Manager authentication and then run the normal docker-stack deploy
command:
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: mesudip/docker-stack@v2
with:
manager: https://manager.example.com:2378
- run: docker-stack deploy --namespace team-a --with-registry-auth my-stack docker-compose.yml
You can also deploy the full compose file through action inputs:
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: mesudip/docker-stack@v2
with:
manager: https://manager.example.com:2378
stack: my-stack
compose-file: docker-compose.yml
namespace: team-a
with-registry-auth: "true"
To release new service images without submitting the compose file again:
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: mesudip/docker-stack@v2
with:
manager: https://manager.example.com:2378
stack: my-stack
namespace: team-a
with-registry-auth: "true"
images: |
api=ghcr.io/acme/api:${{ github.sha }}
worker=ghcr.io/acme/worker:${{ github.sha }}
Use a full compose deployment when the workflow owns the complete stack
definition. Use an image-only deployment when the stack is already managed and
the workflow only needs to release new images. Both forms support namespaces;
the namespace defaults to default when omitted.
Authenticated Docker-Manager shell
Open an isolated Bash or Zsh session for a manager context:
docker-stack shell office
If office does not exist yet, the CLI asks for its Docker-Manager URL, creates
the context, authenticates, and opens the shell. You can also provide everything
non-interactively with docker-stack shell --context office <manager-url>.
Stack commands use the default namespace unless -n/--namespace is supplied.
Listings print the selected namespace; docker-stack ls -A (or
--all-namespaces) lists every visible namespace.
The prompt displays (docker:office@cluster), keeps the selected manager context active,
and refreshes authentication when needed. The session supports docker and
docker compose; legacy docker-compose is not supported. If authentication
expires, run docker-stack login again.
Container listing is cluster-aware and includes the owning Swarm node. Select a node when working with daemon-local resources such as volumes and images:
docker ps
docker-stack node current
docker-stack node use worker-02
docker volume ls
docker image ls
docker-stack node use cluster
Node selection is stored only in the managed shell's isolated Docker
configuration and appears in the prompt as (docker:office@worker-02).
Selected-node requests remain subject to the manager's Docker permissions;
image management is root-only because the manager does not define delegated
image permissions.
Cluster-aware output is enabled after the manager has observed compatible agents on every discovered node. The manager remembers that capability until it restarts, so a temporary agent outage does not make the CLI revert to a legacy feature decision. A partial cluster listing prints the available containers, reports each failed node on stderr with its incident id, and exits non-zero.
docker ps --quiet and docker ps --format ... use Docker's native formatter
and therefore do not add the NODE column. Explicit Docker overrides such as
--context, --host/-H, and --config bypass managed cluster formatting and
are sent unchanged to the Docker CLI.
What It Adds
Beyond docker stack deploy, against a plain Docker daemon:
- Generated secrets — no external scripts; see Object Versioning and Reuse for the once-only semantics.
- Inline and templated configs/secrets — content in the compose file, or expanded from environment variables and files.
- Automatic versioning — configs and secrets are content-hashed and versioned, so edits do not require hand-written
_v2names. - Version inspection and checkout —
versions,cat, andcheckoutrestore a complete recorded stack version, including its configs. - Cluster-aware inspection — stack and node output that reports the owning Swarm node.
Concurrent deploys (Docker-Manager)
Docker-Manager runs one apply per stack at a time. When docker-stack deploy
or docker-stack checkout finds another run in progress (a UI deploy, a CI
image bump, another operator), it waits and prints who started it:
[manager] waiting: a deploy started by alice 42s ago is still running (waiting up to 300s, set DOCKER_MANAGER_DEPLOY_WAIT_SECS to change)
[manager] press Enter twice to force your deploy (aborts that run; changes it already made to the daemon stay), Ctrl+C to quit
- It retries every 5 seconds until the stack is free or
DOCKER_MANAGER_DEPLOY_WAIT_SECSruns out (default: the deploy timeout;0fails immediately). CI runs wait the same way, without the prompt. - At a terminal, pressing Enter twice within 3 seconds asks the manager to abort the running deployment and deploys as soon as the stack is released. This needs stack deploy permission and is offered once per run. The aborted run stops orchestrating, but daemon requests it already made still complete; your deploy then applies over that state.
- Ctrl+C exits without touching the other run.
Compose Extensions
docker-stack reads extra keys under top-level configs: and secrets: and
resolves them into real Docker objects before calling docker stack deploy.
Standard Compose keys (file:, external:, name:) continue to work.
| Key | Configs | Secrets | Content comes from |
|---|---|---|---|
file: |
yes | yes | the file, verbatim (standard Compose) |
x-content |
yes | yes | a literal string in the compose file |
x-template |
yes | yes | a literal string, with ${VAR} expanded |
x-template-file |
yes | yes | a file, with ${VAR} expanded |
environment |
no | yes | the named environment variable |
x-generate |
no | yes | a value generated by docker-stack |
Exactly one content key per object.
x-content — inline content
secrets:
my_inline_secret:
x-content: "This is my secret content defined inline."
configs:
my_inline_config:
x-content: |
key=value
another_key=another_value
x-template and x-template-file — environment substitution
${VAR} references are expanded from the deploying shell's environment.
secrets:
my_templated_secret:
x-template: "${API_KEY_NAME}:${MY_API_KEY}"
configs:
my_config_from_template_file:
x-template-file: "./templates/my_config.tpl"
environment — content from a variable
secrets:
api_token:
environment: API_TOKEN
configs:
app_conf:
environment: APP_CONF_VALUE
If the variable is unset or empty, the deploy fails before any Docker object is created.
Configs are supported on Docker-Manager deploys, where the value travels in the
stack .env and the manager resolves it. A raw daemon deploy supports secrets
only. The two are handled differently on purpose: a secret's value is inlined
locally so it is never written to the .env that is stored with the stack.
x-generate — generated secrets
Secrets only. Configs must use x-content, x-template, or x-template-file.
secrets:
# default options, random length 12-20
simple:
x-generate: true
# fixed length, default character classes
fixed_length:
x-generate: 30
# explicit character classes
api_token:
x-generate:
length: 40
numbers: true # digits 0-9 (default true)
special: true # punctuation (default true)
uppercase: true # A-Z (default true)
Lowercase letters are always included and the value always starts with a letter.
special: true leaves out $, ', " and \, so generated values are safe
to paste into a shell or a YAML file. Use special: false when the application
rejects punctuation, or for values that go in URLs or HTTP headers:
secrets:
bearer_token:
x-generate:
length: 64
numbers: true
special: false
uppercase: true
Versioning and Reuse
Docker configs and secrets cannot be changed in place. docker-stack handles
that for you: when a config's content changes, it creates a new version and
points your services at it. You never have to add _v2 to names yourself.
Generated secrets are created once. Redeploying does not change them, so anything holding a generated value keeps working. The value survives redeploys, restarts and unrelated changes to the stack.
Stored source metadata
Versioned stack configs include a top-level x-files list holding
base64-encoded source material for recovery and auditing: the original compose
file as compose.yml, a generated .env containing referenced non-secret
environment values, and any config files referenced by configs.*.file or
configs.*.x-template-file.
Secret source files, and the variables named by secrets.*.environment, are
deliberately not stored in x-files.
Rotating a generated secret
There is no rotate flag. Remove the secret's newest version, then deploy again to get a fresh value:
docker secret rm <name>_v<N>
docker-stack deploy --show-generated my-stack docker-compose.yml
--show-generated prints the new value. Update anything still holding the old
one.
Secrets you created yourself
If a secret already exists because you ran docker secret create, pointing
x-generate at it does not take it over. The deploy generates a new value
instead, and anything holding the old one stops working.
Either leave it as external: true, or move to x-generate deliberately:
deploy once with --show-generated, then update your clients. After that it
behaves like any other generated secret.
Known Limitations
Docker limits config content to 500 KB. Stack history includes encoded source files, so stacks with large compose or config files can exceed that limit.
Development
Install runtime and test dependencies with either:
python3 -m pip install -r requirements-dev.txt
or:
python3 -m pip install -e '.[dev]'
Metadata
Release files for docker-stack 2.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| docker_stack-2.3.2.tar.gz | 107.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| docker_stack-2.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.3 kB
Release files / docker_stack-2.3.2.tar.gz
| Download URL | docker_stack-2.3.2.tar.gz |
|---|---|
| Size | 107.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f3d6cd067fe2e1c39ab29c0b213b8cfc7c79b635280c21893bbeff5466f2e266
|
|
BLAKE2b-256 checksum How to use checksums |
202ca5b109e1505702b88661d894013f96b2faa0267037f905411e04ff6cee15
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.10.21
|
Release files / docker_stack-2.3.2-py3-none-any.whl
| Download URL | docker_stack-2.3.2-py3-none-any.whl |
|---|---|
| Size | 78.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
aa8e5d008ac581350172a2b3e277789f8eb4b011d2625261956f29e1ca0db86d
|
|
BLAKE2b-256 checksum How to use checksums |
460faad7770c0246374764dd75924860e884756c24556ca2fee1f7f33a9e9436
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.10.21
|
Security for docker-stack 2.3.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.