How to harden a Dragonfly container (and is dragonfly:latest safe for your cache?)¶
Dragonfly is a drop-in Redis and Memcached replacement: it holds session state, cache entries,
queues, and often the working set of data your app reaches for on every request. A stock docker run
dragonflydb/dragonfly:latest holds that data behind a boundary weaker than the data deserves. Graded
on IronClaw's seven-dimension containment scale, the default configuration scores 48 of 100, grade
D (porous). Higher is safer. Unlike a broker or a shared network cache, an in-memory store that only
its co-located application talks to over the loopback can close every dimension, including the
network. A few runtime flags take the same image to a full 100 of 100, grade A. Here are the
exact gaps and fixes from the scan data.
Every number here comes from a read-only
docker inspectofdocker.dragonflydb.io/dragonflydb/dragonfly:latest, the same data behind its isolation scorecard. No workload is executed. How scoring works →
Where the default configuration leaks¶
ironctl scan grades seven independent containment boundaries. On a default docker run
dragonflydb/dragonfly:latest, three fail and one warns:
| Dimension | Verdict | Score | What the scan found |
|---|---|---|---|
| Non-root user (uid != 0) | ❌ FAIL | 0/15 | runs as root (uid 0); a container escape starts with host-uid 0 |
| Dropped capabilities | ❌ FAIL | 4/20 | default capability set retained (CAP_NET_RAW, CAP_MKNOD, and more) |
| Seccomp profile | ✅ PASS | 15/15 | seccomp profile active |
| Network isolation / egress | ⚠️ WARN | 4/15 | network=bridge: outbound egress is possible |
| Read-only root filesystem | ❌ FAIL | 0/10 | root filesystem is writable |
| No docker.sock exposure | ✅ PASS | 15/15 | no control socket mounted |
| No shared host namespaces | ✅ PASS | 10/10 | no host PID/IPC/network sharing |
The two that should worry you most are root and egress. A Dragonfly process that escapes as root escapes as root on the host, next to the very data it was holding in memory and its snapshot files on disk. And a store that can reach arbitrary destinations is one that can quietly ship your cached data out the moment a protocol-parsing CVE lands code execution. The default capability set and writable rootfs widen and entrench that foothold.
Harden it: the exact --fix remediation¶
ironctl scan my-dragonfly --fix prints one remediation per failed dimension, then one hardened run.
For dragonflydb/dragonfly:latest:
--user 65532:65532(Non-root user, +15): pin a non-root uid so an escape does not begin as host uid 0. Point the snapshot directory at a volume this uid owns.--cap-drop=ALL(Dropped capabilities, +16): drop every Linux capability; Dragonfly needs none of the default set to serve its Redis-compatible protocol on a high port.--read-only --tmpfs /tmp(Read-only rootfs, +10): make the root filesystem read-only and mount/dataas an explicit writable volume for snapshots. Removes the persistence surface.--network=none(Network isolation, +11 to the full 15): this is the dimension a co-located store can actually max out. If the only client is the app on the same host or pod and reaches Dragonfly over the loopback of a shared network namespace, cut the NIC entirely. Nothing external can connect, and the store cannot phone home.
When network=none is not honest¶
If Dragonfly is a shared cache that many services across the network connect to, or you run
replication to replicas, you cannot use --network=none; it has to accept those connections. In that
case put it on a user-defined network scoped to just its clients and replicas, with no default route
out. That holds the network dimension at a WARN (4 of 15) and the honest ceiling becomes 89 of 100,
grade B, the same as a broker. Use --network=none only for the single-application, co-located
case.
Before and after¶
# Before: 48/100, grade D
docker run -d --name dragonfly docker.dragonflydb.io/dragonflydb/dragonfly:latest
# After: 100/100, grade A (co-located app on loopback, no network needed)
docker run -d --name dragonfly-hardened \
--user 65532:65532 \
--cap-drop=ALL \
--security-opt=no-new-privileges \
--read-only --tmpfs /tmp \
-v dragonfly-data:/data \
--network=none \
docker.dragonflydb.io/dragonflydb/dragonfly:latest
Rescan: ironctl scan dragonfly-hardened reports 100/100 grade A. A 52-point swing with no
custom image build, just the right flags. Every dimension is closed because a co-located in-memory
store does not need to talk to anything but the app on the other side of its loopback. Run it as a
shared network cache instead and the honest ceiling is 89/100, grade B, called out above.
Verify it on your own Dragonfly¶
# install (Homebrew)
brew install ironsecco/ironclaw/ironclaw
# grade your running container, then print the fixes
ironctl scan my-dragonfly
ironctl scan my-dragonfly --fix
ironctl scan also reads a docker-compose.yml service or a Kubernetes manifest, so you can grade
the Dragonfly in your stack, not just a bare docker run.
Keep going¶
- All hardening guides →: every harden-a-container walkthrough, with grade deltas.
- dragonfly isolation scorecard →: the full dimension breakdown.
- How to harden a Redis container →: the store Dragonfly is a drop-in replacement for, with the same grade-A path.
- Scan any container in 10 seconds →: the full
ironctl scanreference. - Run untrusted code in a real sandbox →: IronClaw wraps every AI-agent session in a gVisor/Kata boundary with
network=noneby default.