14.2.2.1 Secret Manager Use
A focused guide to Secret Manager Use, connecting core concepts with practical Docker and container operations.
Secret manager use for Docker production workloads refers to integrating a dedicated external system, such as Vault, AWS Secrets Manager, Google Secret Manager, or Azure Key Vault, as the authoritative source for credentials, replacing static files and environment variables with values that are fetched on demand, centrally rotated, and access-controlled outside of the container runtime itself.
Why a dedicated secret manager earns its complexity
File-based and Swarm-native secrets solve the problem of keeping a credential off docker inspect output and out of process environments, but they do not solve rotation, fine-grained access policy, or audit logging on their own. A secret manager is a system built specifically around those concerns: it issues short-lived or versioned credentials, enforces who or what is allowed to read a given secret, and records every access, which becomes significantly more valuable as the number of services and the number of people with some level of production access both grow.
Fetching secrets at container startup
The most common integration pattern has the application, or a thin wrapper around it, authenticate to the secret manager and pull the values it needs before the main process starts:
docker run -e VAULT_ADDR=https://vault.example.com \
-e VAULT_ROLE_ID=my-api-role \
-e VAULT_SECRET_ID=my-api-secret-id \
my-api
const vault = require('node-vault')({ endpoint: process.env.VAULT_ADDR });
await vault.approleLogin({
role_id: process.env.VAULT_ROLE_ID,
secret_id: process.env.VAULT_SECRET_ID,
});
const { data } = await vault.read('secret/data/my-api/database');
process.env.DATABASE_URL = data.data.connection_string;
The values used to authenticate to the secret manager itself (a role ID and secret ID in this example) are still credentials that need protecting, but they are narrowly scoped to "permission to fetch this application's secrets," which is a much smaller blast radius than the underlying database password or signing key they unlock.
The entrypoint wrapper pattern
Rather than embedding secret-fetching logic directly in application code, a common pattern uses a thin entrypoint script that fetches secrets, writes them somewhere the application expects, and then execs the real process:
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]
#!/bin/sh
export DATABASE_URL=$(vault kv get -field=connection_string secret/my-api/database)
exec "$@"
This keeps secret-fetching logic out of the application codebase entirely, which makes it easier to reuse across services written in different languages and easier to update independently of an application release.
Sidecar and agent-based injection
A sidecar or agent process, such as Vault Agent, can run alongside the application container and continuously render fetched secrets to a shared, in-memory volume that the application reads from as ordinary files:
services:
vault-agent:
image: hashicorp/vault
command: ["vault", "agent", "-config=/vault/config/agent.hcl"]
volumes:
- secrets:/vault/secrets
api:
image: my-api:latest
volumes:
- secrets:/run/secrets:ro
depends_on:
- vault-agent
volumes:
secrets:
This pattern has the additional benefit of supporting automatic secret renewal: the agent can detect when a fetched secret is approaching expiration and refresh it, updating the shared file without requiring the application to implement any of that logic itself.
Cloud-native secret manager integration
Cloud-managed secret stores typically integrate with the platform's own identity system, so a container can authenticate using its workload identity rather than a separately distributed credential:
aws secretsmanager get-secret-value --secret-id prod/my-api/db-password \
--query SecretString --output text
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const client = new SecretsManagerClient({ region: 'us-east-1' });
const response = await client.send(new GetSecretValueCommand({ SecretId: 'prod/my-api/db-password' }));
When the container runs on infrastructure with an attached IAM role or equivalent identity, no separate bootstrap credential is needed at all; access to the secret is granted directly to the identity the container already runs as.
Caching and failure handling
An application that fetches secrets at startup should treat the secret manager as a dependency that can be temporarily unavailable, with a clear, deliberate decision about what happens if a fetch fails:
let dbPassword;
try {
dbPassword = await fetchSecret('db_password');
} catch (err) {
console.error('Failed to fetch secrets at startup, refusing to start:', err);
process.exit(1);
}
Failing to start is usually the correct behavior for a missing critical secret; silently falling back to a stale cached value or a default can mask a real problem and continue serving traffic with an outdated or incorrect credential.
Scoping access narrowly
A secret manager's value comes largely from how narrowly its access policies are scoped. A policy that grants one application's identity read access to exactly the secrets that application needs, and nothing else, limits the damage a compromised container can do, compared to a broad policy that grants every service access to every secret in the store:
path "secret/data/my-api/*" {
capabilities = ["read"]
}
Common mistakes
- Granting overly broad read access to a secret path, so a single compromised service can read every other service's secrets through the same manager.
- Distributing the credential used to authenticate to the secret manager as carelessly as the secrets it is meant to protect, recreating the original problem one layer removed.
- Treating a failed secret fetch at startup as non-fatal, allowing the application to run with missing or stale credentials.
- Caching fetched secrets indefinitely in application memory without any renewal path, defeating the rotation benefits the secret manager was adopted to provide.
- Adopting a secret manager for new services while leaving older services on plaintext environment variables, leaving the most established and often most sensitive systems with the weakest protection.
Effective secret manager use treats the manager as the single source of truth for credentials, scopes access narrowly per workload identity, fails closed when a fetch cannot succeed, and extends the same discipline consistently across every production service rather than only the newest ones.