March 19, 2026
Docker Swarm Secrets: Create, Rotate, Lock and Inspect Them Safely
The reference for Docker Swarm secrets: how they work, the _FILE convention, stack-file syntax, zero-downtime rotation, autolock, and how to inspect a value when you must.

Docker Swarm secrets are the right way to get a database password, an API key or a TLS private key into a container: encrypted in the managers' Raft log, delivered to exactly the nodes that run a task that needs them, mounted from memory, never written to disk on a worker. They are also, by design, write-only, which is where most teams get stuck. This is the reference for using them well: creating, wiring into a stack, rotating without downtime, locking the store, and inspecting a value when you must.
How Docker Swarm secrets work
A secret is a blob of up to 500 KB stored in the swarm's Raft log, encrypted at rest with a key the managers hold. When a service that references the secret schedules a task on a node, the manager sends the secret to that node over the mutually authenticated TLS connection every node already has, and the engine mounts it into the container as a file under /run/secrets/<name>, on a tmpfs, owned by the uid and mode you set. When the task stops, the node drops the secret. A node that runs no task needing the secret never receives it.
Three consequences follow. Secrets only work for services, not for docker run. A secret is immutable: there is no update, only create and remove. And the value is never returned by the API: docker secret inspect shows the name, dates and labels, nothing else.
Compare that with environment variables in a Compose file: visible in docker inspect, in ps on the host, in every log line that dumps the environment, and copied into any image that bakes them in.
Creating a secret
From standard input, which keeps the value out of your shell history:
printf '%s' 'S3cr3t-pa55w0rd' | docker secret create db_password -
From a file, which is right for certificates and keys:
docker secret create site_key ./site.key
docker secret create site_crt ./site.crt
Labels help later, when you have forty of them:
docker secret create --label app=shop --label rotation=2026-09 db_password_v2 -
Using a secret in a stack file
Declare the secret at the top level, then reference it from the service. external: true means "already created on the swarm", which is what you want for anything real; file: reads the value from disk at deploy time and is fine for local development.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
web:
image: shop/web:2.1.0
secrets:
- source: site_key
target: /etc/ssl/private/site.key
uid: '33'
gid: '33'
mode: 0400
secrets:
db_password:
external: true
site_key:
external: true
The _FILE convention matters: the official Postgres, MySQL, MariaDB and Redis images, and most well-behaved applications, read SOMETHING_FILE and load the value from that path. If your own service does not, add ten lines to read the file at start-up. Do not read it into an environment variable in an entrypoint script and export it; you have just undone the whole mechanism.
The long syntax sets where the file lands and who can read it. Default is /run/secrets/<name>, root-owned, mode 0444, which is readable by every user in the container. Set the uid and mode for anything a non-root process reads.
Rotating a secret without downtime
Because a secret cannot be changed, rotation means a new secret and a rolling update that swaps the reference. Version the name:
printf '%s' 'new-pa55w0rd' | docker secret create db_password_v2 -
docker service update \
--secret-rm db_password \
--secret-add source=db_password_v2,target=db_password \
shop_db
The target=db_password keeps the file path inside the container stable, so the application does not change. The service rolls one task at a time under its update_config, and when every task runs with the new file, the old secret has no references and can be removed:
docker secret rm db_password
In a stack file, the same thing is a rename of the top-level key plus the source: line, then docker stack deploy again. Keep the versioned name in the file; it is the audit trail.
Two things to plan for. The application has to reread its credential on restart rather than caching it forever, which is what a rolling update gives you for free. And the service that checks the credential, the database, needs to accept both values during the roll, so rotate the database user first, then the consumers, then retire the old value.
Configs are the same mechanism, unencrypted
docker config works identically, mounted at the path you choose, for things that are not secret: nginx.conf, a Prometheus scrape file, a feature-flag JSON. Use configs for those and keep secrets for credentials; it keeps the encrypted store small and makes "what is sensitive here" answerable from the stack file alone.
Lock the store
The Raft log is encrypted, but by default the key that decrypts it sits on each manager's disk, so a copied manager disk is a copied secret store. Autolock keeps that key in memory only:
docker swarm update --autolock=true
Docker prints an unlock key. After every manager restart, someone runs docker swarm unlock and pastes it, or the manager stays out of the quorum. That is the trade: stronger at-rest protection against a manual step on reboot. Store the unlock key where you store the root password, and rotate it with docker swarm unlock-key --rotate.
Inspecting a value when you have to
"Write-once, read-never" is a feature until the day a deploy fails and you need to know whether the secret you created last week actually holds what you think. Natively, you deploy a throwaway service that mounts it and cat the file, then remember to remove the service.
SwarmCLI does that dance for you. Highlight a secret and press x: it creates a short-lived swarmcli-reveal-* service (alpine:latest by default) whose single task runs one container that cats /run/secrets/<name> and exits. It has to be a service rather than a bare docker run, because Swarm injects a secret into a task — a container started outside one has nothing to mount. SwarmCLI reads the value back out of that task's logs into your terminal and removes the service. It is pinned to the node you are connected to and forced onto the json-file log driver, so the plaintext stays on that node rather than travelling to whatever aggregator the host ships to — it does pass through that node's container log for the life of the task, which is why a reveal is worth auditing. In a swarm behind the SwarmCLI proxy the reveal is an audited action tied to a user. The helper image is configurable through SWARMCLI_REVEAL_IMAGE for environments that only allow hardened base images.
The same view lists every secret with its age, which services reference it, and which are orphaned, which is the "is this still used?" question docker secret ls cannot answer.
Mistakes worth naming
- Values in
environment:. Visible everywhere, forever. Use_FILE. - Creating from a shell argument.
docker secret create x "value"is not a thing, butecho "value" | docker secret create x -puts the value in your history. Useprintffrom a file or a password manager's CLI, orhistory -d. - Baking credentials into images. They stay in every layer of every registry copy.
- Logging the environment at start-up. Many frameworks do. With
_FILEthere is nothing to leak, which is the second reason to use it. - Mounting a secret into a global service that does not need it. Every node then holds it. Scope references to the services that read them.
- Forgetting managers hold everything. Manager disks are the secret store. Encrypt them, autolock the swarm, and treat manager backups as sensitive.
External secret managers
If you already run Vault, SOPS, or a cloud secret manager, keep it as the source of truth and use Swarm secrets as the delivery layer: a CI step fetches the value and runs docker secret create <name>_v<n>, and the stack file references external: true. The application still reads a file; the swarm still delivers it only where needed; the rotation procedure above still applies.
Conclusion
Swarm's secret mechanism is small and correct. Use it through the _FILE convention, rotate by version and rolling update, lock the store on anything that matters, and reach for the reveal only when you need to check a value. The Definitive Docker Swarm Guide covers the networking half; the mTLS post covers who is allowed to talk to the daemon at all.
2026 Docker Swarm Mastery Series
- Mar 9: [The Foundation] The Definitive Docker Swarm Guide for 2026.
- Mar 12: [Expert Analysis] Docker Swarm vs Kubernetes in 2026 — The Case for Staying Simple.
- Mar 16: [Edge Frontier] Setting up the ultimate 3-node Swarm on Raspberry Pi 5.
- Mar 19: [Security Specialist] Secure by Design: Managing Docker Swarm Secrets the SwarmCLI Way.
- Mar 23: [Ops Mastery] Docker Swarm Auto-healing: A Guide to Troubleshooting 'Pending' States.