Docker Secrets Management: Stop Hardcoding Credentials in Production
Docker secrets management explained with production patterns.
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
- ✓Docker basics (images, containers, services)
- ✓Docker Swarm or Kubernetes fundamentals
- ✓Familiarity with environment variables in Docker
Use Docker's built-in docker secret create for Swarm services, or mount secrets as files via bind mounts with restricted permissions. Never pass secrets as environment variables in production — they're visible in process lists and logs.
Think of Docker secrets like a hotel safe deposit box. You (the container) get a key to open the box when you check in, but the box's contents are never written on the key itself. If someone steals the key, they still can't read the contents without physically opening the box. Similarly, Docker secrets are stored encrypted at rest and only decrypted in memory inside the container that needs them.
I've seen a startup's entire database credentials leaked because someone ran docker inspect on a running container. The password was in an environment variable. That's not a rookie mistake — it's a design flaw. Docker Secrets Management exists precisely to prevent this. It's not about convenience; it's about not getting pwned at 3 AM when an attacker enumerates your containers. After this article, you'll be able to design a secret delivery pipeline that survives audits, rotates keys without downtime, and doesn't make you cringe when ops asks for the secret inventory.
Why Environment Variables Are a Security Nightmare
Environment variables are the default way to pass configuration to containers. They're easy, they're documented everywhere, and they're wrong for secrets. Here's why: any process with access to /proc/<pid>/environ can read them. That includes docker inspect, kubectl exec, and any compromised container on the same host. Worse, env vars leak into logs, error reports, and core dumps. I've personally debugged a case where a Python traceback printed the entire DB password because it was in the environment. The fix? Never put secrets in env vars in production. Use Docker secrets or a vault.
Docker Swarm Secrets: The Built-In Solution
Docker Swarm has native secret management. Secrets are encrypted at rest in the Swarm store and only decrypted when a container that needs them is scheduled. They're mounted as files in /run/secrets/ by default, with in-memory tmpfs. No disk writes. No env var leaks. The flow: docker secret create stores the secret, then you grant access to services via --secret. The container reads the file at startup. Rotation requires updating the secret and redeploying the service. For zero-downtime rotation, you can mount multiple versions and have the app watch for changes.
docker secret create with --label to track metadata like rotation date or owner. It's a lifesaver when auditors ask for an inventory.Kubernetes Secrets: More Power, More Pitfalls
Kubernetes Secrets are similar but more flexible — and more dangerous. By default, Secrets are stored unencrypted in etcd. You must enable encryption at rest. They can be mounted as volumes or injected as env vars (don't do the latter). The real power is in integration with external vaults like HashiCorp Vault via CSI drivers or sidecars. For production, never use plain Kubernetes Secrets for anything sensitive. Always enable encryption and use RBAC to restrict access. The classic mistake: storing a cloud API key in a Secret and then accidentally committing the YAML to git.
envFrom with a Secret. It creates env vars that leak via /proc and kubectl exec. Always mount as a volume.Secret Rotation Without Downtime
Rotating secrets is where most teams fail. The naive approach: update the secret, restart all containers. That causes downtime. Better: mount the secret as a file and have the application watch for file changes (e.g., inotify). When the file changes, reload the connection. For databases, use a connection pool that supports credential refresh (like HikariCP with a DataSource that reads from a file). In Swarm, you can update a secret and redeploy the service with --update-delay for rolling updates. In Kubernetes, use a controller that watches the Secret and triggers a rolling restart via annotation change.
External Vault Integration: HashiCorp Vault with Docker
For serious production environments, built-in secrets managers are too limited. You need audit logs, dynamic secrets (short-lived credentials), and fine-grained access control. HashiCorp Vault is the gold standard. Integrate with Docker via the Vault agent sidecar pattern: a container that authenticates to Vault, retrieves secrets, and writes them to a shared volume. The application reads from that volume. Vault handles rotation automatically. The downside: complexity. You now manage Vault itself. But for compliance-heavy environments (PCI-DSS, HIPAA), it's non-negotiable.
When Not to Use Docker Secrets
Docker secrets are overkill for local development. Use .env files or docker-compose's env_file — just don't commit them to git. For single-node deployments, bind-mounting a secrets directory with restricted permissions is simpler and equally secure. Also, if your orchestrator doesn't support secrets (e.g., plain Docker Compose without Swarm), you're stuck with workarounds. In those cases, use a vault sidecar or encrypt the env var with a key that the app decrypts at startup. The key itself must be stored securely — a chicken-and-egg problem that vault solves.
.secrets directory with a .gitignore entry. Add a secrets.example file with placeholder values for onboarding.The 4GB Container That Kept Dying
- Never let your application re-read secrets from environment variables on every request — it's a performance and reliability trap.
docker secret ls to verify secret exists. 2. Check service definition for correct secret name. 3. Ensure service is in the same Swarm as the secret.docker exec <container> ls -la /run/secrets/. 2. Check if secret was created with trailing newline — use printf instead of echo. 3. Ensure file permissions allow read by the runtime user.docker secret inspect <secret>. 2. Redeploy the service: docker service update --secret-rm <old> --secret-add <new> <service>. 3. For zero-downtime, use rolling update with --update-delay.docker secret lsdocker service ps <service> --no-trunc | grep secretecho 'password' | docker secret create <name> -| File | Command / Code | Purpose |
|---|---|---|
| env_var_leak.sh | docker run -d --name leaky -e DB_PASSWORD=supersecret alpine sleep 3600 | Why Environment Variables Are a Security Nightmare |
| swarm_secret_setup.sh | echo "my-db-password" | docker secret create db_password - | Docker Swarm Secrets |
| k8s_secret.yaml | apiVersion: v1 | Kubernetes Secrets |
| secret_watcher.py | SECRET_PATH = '/run/secrets/db_password' | Secret Rotation Without Downtime |
| docker-compose.vault.yml | version: '3.8' | External Vault Integration |
| local_secret_mount.sh | mkdir -p ./secrets | When Not to Use Docker Secrets |
Key takeaways
Interview Questions on This Topic
How does Docker Swarm handle secret encryption at rest and in transit?
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
That's Docker. Mark it forged?
3 min read · try the examples if you haven't