
September 10, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Hard-coded passwords in docker-compose.yml files cause real breaches. Docker Secrets in Compose and Swarm give you a built-in way to inject credentials at runtime without baking them into images or plain environment variables. Swarm encrypts secrets in its Raft store and mounts them as read-only files under /run/secrets/. Compose supports the same file-mount pattern for local stacks. If you run multi-container Docker Compose apps or small Swarm clusters on Ubuntu, this guide covers the commands, YAML, and production habits that actually stick.
/run/secrets/<name> inside containers. Create secrets with docker secret create in Swarm, or reference local files in Compose, and read them from disk in your app—not from ENV.What Are Docker Secrets in Compose and Swarm?
Docker secrets are named blobs of sensitive data. Swarm distributes them only to tasks that declare a need. Compose maps the same concept to files on your dev machine. Neither approach puts the raw value in your image layers or in docker inspect output the way environment: blocks do.
In Swarm mode, secrets are encrypted at rest in the cluster state. They travel over mutual TLS between manager and worker nodes. Inside a running container, Docker mounts each secret on a tmpfs filesystem. The file is world-readable inside the container namespace, but it never lands on the host disk outside that mount.
Compose without Swarm still benefits from the secrets block. You define a file path or external name, and Compose bind-mounts it into the container at the standard path. That keeps your Laravel .env database password out of Git while you follow a local Laravel Docker Compose workflow.
Secrets vs environment variables vs bind mounts
Environment variables show up in process listings and child shells. Bind mounts from host paths leak files onto disk that backups and log scrapers can touch. Secrets target a narrow middle ground: file-based consumption with tmpfs backing in Swarm.
| Method | Visible in docker inspect | On host disk | Swarm encrypted transit | Best for |
|---|---|---|---|---|
environment: | Yes (value exposed) | N/A | No | Non-sensitive config flags |
| Bind mount file | Mount path only | Yes | No | Dev-only shortcuts |
| Docker secret | Name only | No (tmpfs) | Yes | DB passwords, API keys, TLS keys |
| External vault | Depends on sidecar | Depends | Depends | Multi-cluster, rotation at scale |
For larger fleets, pair Swarm secrets with HashiCorp Vault for secrets management or an external secrets operator. Swarm secrets solve single-cluster delivery. Vault solves issuance, audit, and dynamic credentials.
How Do You Create and List Secrets in Docker Swarm?
Swarm secrets require an active Swarm manager. Initialize one if needed, then create secrets from stdin, a file, or a pipe. Names are immutable; updates require a new secret name and a service redeploy.
Initialize Swarm and create secrets
- Run
docker swarm initon your manager node after you install Docker on Ubuntu. - Create a secret from a file or stdin:
# From stdin (preferred — avoids leaving files on disk)
printf 'SuperSecretDbPass2026' | docker secret create db_password -
# From a file (delete the file immediately after)
docker secret create api_key ./api_key.txt
# List secrets (names and IDs only — never values)
docker secret ls Remove a secret only when no running service references it:
docker secret rm db_password Inspect metadata without revealing the payload:
docker secret inspect db_password --format '{{ .Spec.Name }}' Attach secrets to a service
Standalone docker service create accepts --secret flags. Compose with deploy: blocks is easier for multi-service stacks.
docker service create \
--name web \
--secret db_password \
--secret source=api_key,target=payment_key,mode=0440 \
myregistry/laravel-app:2026 The optional target renames the in-container filename. The optional mode sets Unix permissions. Default mode is 0444 (read-only for all users in the container).
How Do You Configure Docker Compose Secrets for Local and Swarm Stacks?
Compose file format 3.1 and later supports a top-level secrets: key. Services reference secret names; Compose wires the mount. Behaviour differs slightly between docker compose up on a laptop and docker stack deploy on Swarm.
Local Compose with file-based secrets
Store secret files outside version control. Add secrets/ to .gitignore. Reference them in Compose:
services:
app:
image: laravel-app:local
secrets:
- db_password
- redis_password
environment:
APP_ENV: local
networks:
- backend
db:
image: mysql:9.7
environment:
MYSQL_DATABASE: app
secrets:
- db_password
# Custom entrypoint reads /run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
redis_password:
file: ./secrets/redis_password.txt
networks:
backend: Run the stack:
docker compose up -d For override patterns—different secret paths per developer—use Docker Compose profiles and overrides instead of duplicating entire compose files.
Swarm stack deploy with external secrets
When you deploy with docker stack deploy, secrets must already exist in Swarm. Compose marks them as external: true.
services:
app:
image: registry.example.com/laravel:13
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
secrets:
- db_password
- app_key
networks:
- web
secrets:
db_password:
external: true
app_key:
external: true
networks:
web:
driver: overlay Deploy sequence:
printf 'prod-db-pass' | docker secret create db_password -
printf 'base64-app-key' | docker secret create app_key -
docker stack deploy -c docker-compose.prod.yml myapp I've used this pattern on shared EC2 infrastructure where several sites share a Deployer and GitLab CI pipeline. Secrets are created once per environment on the Swarm manager. CI never prints values—only checks that names exist before deploy.
How Should Your Application Read Docker Secrets?
Applications should treat /run/secrets/<name> like any other credential file. Read once at boot, cache in memory, and never log the contents. Strip trailing newlines—Docker does not trim stdin payloads.
PHP and Laravel example
Laravel 13 on PHP 8.3+ can read a secret before the config cache builds:
<?php
// config/database.php — read Swarm/Compose secret when present
$secretPath = '/run/secrets/db_password';
$dbPassword = file_exists($secretPath)
? trim(file_get_contents($secretPath))
: env('DB_PASSWORD');
return [
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'db'),
'password' => $dbPassword,
// ...
],
],
]; Do not run php artisan config:cache with a baked-in password from .env on production Swarm nodes. Cache config after secrets are mounted, or keep password resolution dynamic as shown above. On booking systems like Adventure Third Pole Trek, credential rotation without redeploying code saved maintenance windows.
Entrypoint scripts for official images
MySQL and PostgreSQL official images expect env vars, not secret files. A thin entrypoint wrapper bridges the gap:
#!/bin/sh
set -e
export MYSQL_ROOT_PASSWORD="$(cat /run/secrets/db_password)"
exec docker-entrypoint.sh mysqld Mount the script via a custom image layer or a config object in Swarm. Keep the script in Git; keep secrets out.
Node.js and generic shells
const fs = require('fs');
const dbPass = fs.readFileSync('/run/secrets/db_password', 'utf8').trim(); Validate file existence early. A missing secret should fail fast with a clear error—not fall back to an empty password.
What Security Pitfalls Break Docker Secrets in Production?
Secrets solve delivery, not every security problem. Teams still leak credentials through logs, images, and stale secret names. Treat the items below as a pre-deploy checklist.
Common mistakes
- Committing
secrets/*.txtbecause someone copied a tutorial path without.gitignorerules. Run secrets scanning in Git and CI with gitleaks on every pipeline. - Using
ARGin Dockerfiles for passwords. Build args persist in image history. Pass secrets only at runtime. - Assuming Swarm secrets rotate in place. They do not. Create
db_password_v2, update the service, then remove the old secret. - Granting every service every secret. Attach only what each task needs—same least-privilege mindset as RBAC in Laravel.
- Storing TLS private keys as secrets but serving HTTP anyway. Pair secrets with proper Docker networking and edge TLS termination.
Swarm secrets are unavailable on plain docker compose up without Swarm unless you use the file-based Compose secrets block. Do not confuse the two modes during a handoff from dev laptop to production cluster. Document which secret names exist on the manager.
Rotation workflow
- Create a new secret with a version suffix.
- Update database credentials on the server side.
- Redeploy the service with the new secret reference.
- Confirm health checks pass.
- Remove the old secret after all tasks roll forward.
For databases that support dual-password windows, rotation is painless. For static API keys at payment gateways—eSewa, Khalti, Stripe—schedule maintenance and test callbacks in staging first.
When Swarm secrets are not enough
Swarm mode is declining in greenfield projects. Many teams choose Kubernetes or stay on single-node Compose with external vaults. Compare orchestrators in Kubernetes vs Docker Swarm when to use which before investing in Swarm-specific secret tooling.
If you stay on Swarm, integrate CI secrets separately. GitLab CI masked variables can pipe into docker secret create during deploy jobs. Follow broader CI/CD secrets management best practices so build logs stay clean. Ansible users often encrypt files with Ansible Vault for secrets before a playbook creates Docker secrets on the target host.
How Do Docker Secrets Fit a Laravel or Small-Team Deploy Pipeline?
Most of my client stacks still run Compose on a single VPS or a two-node Swarm. The workflow is boring on purpose. Developers use file secrets locally. Production CI creates Swarm secrets over SSH. Deployer symlinks releases; Docker mounts credentials at runtime.
Generate strong local dev passwords with a secure password generator. Never reuse production values on a laptop. For enterprise apps with audit requirements, see enterprise application development services that wire vault backends when Swarm alone is too thin.
A practical docker-compose.override.yml for developers might point at sample secrets while production compose stays external-only. Combine with Docker Compose multi-container local setup guidance so new hires boot the stack in one command.
Health checks and secret-dependent startup
If your app exits when a secret file is missing, set Compose health checks after the entrypoint reads credentials. See Docker healthchecks explained for timing that avoids flapping during slow DB restarts.
Linux host hardening still matters. Secrets do not replace firewall rules, fail2ban, or patched kernels. Linux system administration covers the host layer Swarm sits on.
Alternatives worth knowing
Vault dynamic secrets for databases issue short-lived credentials—better than static files for high-churn teams. External Secrets Operator with Vault targets Kubernetes, not Swarm, but the read path in your app stays similar: read a file, connect, discard.
For teams migrating off Swarm, export secret names and rotation runbooks before you touch orchestration. Document every consumer that reads /run/secrets/. A missed reference becomes a midnight outage.
Ongoing ops—monitoring failed deploys, cleaning orphaned secrets, renewing TLS—fits support and maintenance retainers. Secret hygiene is monthly housekeeping, not a one-time migration.
Key Takeaways
- Create Swarm secrets with
docker secret create; usefile:mappings in Compose for local dev andexternal: truefor stack deploys. - Read credentials from
/run/secrets/<name>in application code—never fromenvironment:for passwords or API keys. - Rotate by creating versioned secret names and rolling services forward; Swarm does not support in-place secret edits.
- Keep
secrets/out of Git, scan repos in CI, and avoidARGfor sensitive build-time values. - Pair Docker secrets with vault tools when you need audit trails, dynamic DB credentials, or multi-cluster delivery.
- Document which services consume which secret names before handoff—missing mounts fail silently until the app crashes.
People Also Ask
Can Docker Compose use secrets without Swarm mode?
Yes. Compose mounts file-based secrets into containers on a single Docker engine. You do not need docker swarm init for local development. Production Swarm deploys require secrets to exist in the cluster before docker stack deploy.
Are Docker Swarm secrets encrypted?
Yes. Swarm encrypts secrets at rest in the Raft log and transmits them over TLS to worker nodes. Inside containers they live on tmpfs. They are not encrypted separately inside the container filesystem—any process in that container namespace can read the mounted file.
What is the difference between Docker configs and secrets?
Both mount as files under /run/. Configs hold non-sensitive data like nginx.conf. Secrets hold credentials and keys. Swarm stores both in the cluster state, but secrets get stricter handling and separate CLI commands. Do not put passwords in configs.
How do I update a Docker secret without downtime?
Add a new secret with a new name, update the service to reference it alongside or instead of the old one, and use rolling updates with health checks. Remove the old secret only after every task uses the new mount. Plan database password changes to allow overlap when possible.
Ship Credentials the Right Way
Docker Secrets in Compose and Swarm are the native path to keep passwords and keys out of images, Git, and casual docker inspect output. Start with file secrets locally, promote to Swarm externals in production, and read everything from /run/secrets/ in your app bootstrap. If you want help wiring secrets into a Laravel stack, CI pipeline, or small Swarm cluster on Ubuntu, contact us or explore custom software development for a deployment review.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

