Docker Swarm can manage secrets, but only if you treat them like any other piece of production data and use a dedicated secrets management system like HashiCorp Vault.
Let’s see how this actually plays out. Imagine a web application running on Swarm. It needs a database password and an API key.
# docker-compose.yml (simplified)
version: '3.8'
services:
webapp:
image: my-webapp:latest
secrets:
- db_password
- api_key
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
API_KEY_FILE: /run/secrets/api_key
secrets:
db_password:
external: true
api_key:
external: true
In this docker-compose.yml, external: true tells Swarm that these secrets aren’t defined within the compose file itself but will be provided externally. Swarm will then mount these secrets as files inside the webapp container at /run/secrets/. The application code reads these files to get the sensitive data.
The "external" part is crucial. Swarm itself can store secrets, but it’s not designed for robust, audited, and versioned secrets management. It’s more of a basic encryption and distribution mechanism. For production, you need something more.
This is where HashiCorp Vault shines. Vault acts as a central, secure "key-value store" for secrets. It can encrypt secrets at rest and in transit, provides fine-grained access control, and audits every access.
Here’s the flow:
-
Vault Setup: You install and configure Vault. You define "secrets engines" (like Key/Value v2) and store your secrets. For example, you might store
db_passwordundersecret/data/myapp/databaseandapi_keyundersecret/data/myapp/api. -
Swarm Integration: You need a way for Swarm services to get these secrets from Vault. The most common and secure method is to use a sidecar container or an init container that’s specifically designed to fetch secrets from Vault and make them available to your main application container. A popular tool for this is
vault-agentorrelayd(from the Vault ecosystem).Let’s consider using
vault-agentas a sidecar. Yourdocker-compose.ymlwould look something like this:# docker-compose.yml with Vault integration version: '3.8' services: webapp: image: my-webapp:latest secrets: - db_password_file # This will be a file created by the sidecar - api_key_file # This will be a file created by the sidecar environment: DB_PASSWORD_FILE: /run/secrets/db_password_file API_KEY_FILE: /run/secrets/api_key_file vault-agent-sidecar: image: hashicorp/vault-agent:latest volumes: - vault-agent-config:/vault/config # Mount the configuration - secrets-store:/run/secrets # Mount a shared volume for secrets environment: # Environment variables to configure the agent (e.g., Vault address, auth method) VAULT_ADDR: "https://vault.mydomain.com:8200" VAULT_ROLE_ID: "your-role-id" # Example: AppRole auth VAULT_SECRET_ID: "your-secret-id" # Example: AppRole auth volumes: vault-agent-config: driver: local secrets-store: driver: local secrets: db_password_file: file: /path/to/dummy/db_password.txt # This is a placeholder, the real secret comes from the sidecar api_key_file: file: /path/to/dummy/api_key.txt # This is a placeholderThe
vault-agent-sidecarservice would have a configuration file (mounted viavault-agent-config) that tells it:- Where Vault is (
VAULT_ADDR). - How to authenticate (e.g., using AppRole, Kubernetes auth, etc.).
- Which secrets to fetch (e.g.,
secret/data/myapp/databaseandsecret/data/myapp/api). - Where to write these fetched secrets (e.g., into the shared
secrets-storevolume, which is then accessible by thewebappservice via thesecrets-storevolume mount).
The
webappservice declares the secrets it needs, but instead of pointing to Swarm secrets, it points to files that thevault-agent-sidecarwill create in the shared volume. - Where Vault is (
-
Secret Rendering: The
vault-agentauthenticates with Vault, reads the specified secrets, and writes them as files (e.g.,db_password_file,api_key_file) into the/run/secretsdirectory within the shared volume. -
Application Access: The
webappservice then reads these files from/run/secretsjust as it would with Swarm-managed secrets.
The key insight here is that Swarm becomes the orchestrator of your application, and Vault becomes the authority for your secrets. Swarm’s role secrets are minimal; they are just encrypted blobs that Swarm distributes. Vault, on the other hand, provides the robust security, auditing, and lifecycle management your production secrets demand.
When you deploy this, Swarm will start both the webapp and the vault-agent-sidecar services. The sidecar will run first, fetch the secrets, and populate the shared volume. Then, the webapp service will start, mount that volume, and find the secret files ready for it.
The most surprising thing about using Vault with Swarm is that you often don’t need Swarm’s native secret management at all. You can configure Vault to write secrets directly into a shared volume that your application service then reads, bypassing Swarm’s secrets: directive entirely. This gives you a more direct, and often simpler, integration path where Vault is the sole source of truth for secrets, and your application just consumes them from a predictable location.
This pattern allows you to manage secrets with enterprise-grade security and compliance features without overburdening your orchestration layer.
The next natural step is to explore automated secret rotation and dynamic secrets with Vault.