skip to content

What does mounting `tmpfs` inside a container buy you for secret material, and how do Docker Compose and Swarm `secrets:` entries actually deliver a value into a container?

level: middleimportance: nice to knowfreq 28%

answer

  1. tmpfs = memory-backed, not in commit/diff, gone on stop
  2. options: noexec,nosuid,mode=0700,size
  3. secrets mount at /run/secrets/<name>, read-only
  4. Compose file: source = bind mount, no encryption
  5. Swarm: encrypted raft, mTLS to the node, immutable

basics

~20 s

A tmpfs mount is memory-backed: nothing written there reaches the container's writable layer or the host disk, and it vanishes when the container stops. Compose and Swarm secrets mount each value read-only at /run/secrets/<name> — Compose from a local file, Swarm from the encrypted raft store over mutual TLS.

solid answer

~50 s

**tmpfs.** `--tmpfs /run/secrets:rw,noexec,nosuid,mode=0700,size=1m` (or `--mount type=tmpfs,...`) gives an in-memory filesystem. Nothing there is captured by `docker commit`, appears in `docker diff`, or survives a restart, and it is never written to the host's disk (barring swap, so disable swap or lock memory where it matters). It is the right home for material an entrypoint fetches or renders at startup. **Compose/Swarm secrets.** You declare a top-level `secrets:` block and reference it per service. The container gets the value as a read-only file at `/run/secrets/<name>`. In plain Compose the source is a local `file:` (or `environment:`) and the delivery is essentially a bind mount — convenient, not a secret store. In **Swarm** it is a managed object: `docker secret create` stores it encrypted in the raft log, the manager delivers it only to nodes running tasks that need it over mutual TLS, and it lands on an in-memory filesystem in the task. Swarm secrets are immutable, so rotation means creating a new secret and updating the service.

code

bash · 11 lines
bash
docker run -d --name api \
  --tmpfs /run/secrets:rw,noexec,nosuid,mode=0700,size=1m \
  -e DB_PASSWORD_FILE=/run/secrets/db_password app:1.0

printf 's3cr3t' | docker secret create db_password -
docker service create --name api --secret db_password app:1.0

# rotation: secrets are immutable, so replace and update
printf 'n3w' | docker secret create db_password_v2 -
docker service update --secret-rm db_password \
  --secret-add source=db_password_v2,target=db_password api

go deeper

for a junior

Know that tmpfs is in-memory storage and that secrets appear as read-only files under /run/secrets.

for a middle

Explain why tmpfs keeps material out of layers and host disk, list the mount options, and describe the Compose vs Swarm delivery difference.

for a senior

Add rotation mechanics, per-service scoping, uid/mode for non-root processes, and the swap caveat.

for a principal

Generalize the shape — small managed object, scoped delivery, in-memory file, rotate by replacement — as the platform contract applications should code against.

## tmpfs, precisely `tmpfs` is a Linux filesystem that lives in page cache — memory, with pages eligible for swap unless swap is off or the memory is locked. Mounting it into a container gives a writable path whose contents never touch the container's copy-on-write layer or a host directory. That matters for secrets in three ways: an image built from a running container (`docker commit`) cannot capture it; `docker diff` does not report it, so it will not sneak into an exported artifact; and container removal destroys it, so no cleanup step can be forgotten. Use the standard hardening options: `noexec` and `nosuid` (nothing there should be executable), `mode=0700` so only the intended uid can traverse it, and `size` to bound the memory a container can consume this way — without a size limit a container can pin a large amount of host memory. The typical pattern: an entrypoint or init sidecar authenticates with a workload identity, fetches the credential, writes it into the tmpfs path, and the application reads that path. Nothing durable ever exists. ## Compose secrets Compose supports a top-level `secrets:` block with a `file:` source (or `environment:`, taking the value from a variable in the shell running Compose), referenced by services. Each referenced secret appears in the container at `/run/secrets/<name>`, read-only. The long syntax lets you override `target`, `uid`, `gid` and `mode`, which matters when the app runs as a non-root user. Be honest about what this is on a single host: the source is a plain file on your disk and Compose mounts it. It buys a **consistent shape** — files at `/run/secrets`, matching the `*_FILE` convention official images use — not encryption at rest. Its value is that the same manifest works when that shape is backed by a real secret store elsewhere. ## Swarm secrets In Swarm mode the object is managed by the cluster. `docker secret create db_password ./file` stores it in the raft log, **encrypted at rest** (the raft log is encrypted, and the cluster can be locked with an unlock key so the keys are unavailable after a restart without it). When a task referencing the secret is scheduled, the manager sends the value **only to that node** over the cluster's mutual-TLS channel, and the value is mounted into the task container on an in-memory filesystem at `/run/secrets/<name>`. It is never written to the node's disk, and when the task stops the node discards it. Operational properties worth naming: secrets are **immutable** — you cannot update one in place, so rotation is `docker secret create db_password_v2` followed by `docker service update --secret-rm db_password --secret-add db_password_v2`, which rolls tasks; access is per-service, so a compromised service only sees what it was granted; and secrets are size-limited (on the order of half a megabyte), so they are for credentials and small certificates, not bulk data. ## Where this fits Swarm has faded relative to Kubernetes, so treat the Swarm detail as background rather than a core skill — but the *shape* is exactly what other platforms adopted: a small managed object, delivered only to the workloads that need it, mounted as a file on an in-memory filesystem, and rotated by replacement. Explaining that shape, and why tmpfs is the right backing for it, is what the question is really testing.

  • Is a Compose secret on a single host meaningfully more secure than a bind-mounted file?
    Mechanically it is close to the same thing: the source is a local file and it is mounted into the container. The gains are consistency and hygiene — a standard read-only path at /run/secrets, per-service scoping in the manifest, and explicit uid/gid/mode — which makes the manifest portable to a platform where the same shape is backed by a real secret store. Do not claim it provides encryption at rest.
  • How do you rotate a Swarm secret without downtime?
    Swarm secrets are immutable, so you create a new secret object and update the service to remove the old reference and add the new one, mapped to the same target path. That triggers a rolling update of the tasks, so with more than one replica and a sensible update policy the service stays available. The old secret can be removed once no service references it.

saying these in an interview costs you the question

  • Believing a Compose file-backed secret is encrypted at rest
  • Mounting tmpfs without a size limit, letting a container pin host memory
  • Assuming tmpfs contents can never reach disk — pages can swap unless swap is disabled or memory locked
  • Trying to update a Swarm secret in place instead of creating a replacement
  • Forgetting uid/mode, so a non-root container process cannot read its own secret file

context