Ephemeral Nature of Containers
Spin up a container, write some data, remove the container — and just like that, everything’s gone. That’s not Docker being careless, it’s Docker being exactly what it’s designed to be: disposable. In this section, we’ll unpack why containers are built this way, why that’s usually a feature and not a bug, and what to do on the rare occasions it does bite you — using bind mounts, volumes, and tmpfs to keep the data that actually matters.
Why Docker Containers Are Ephemeral?
Before anything else, let’s get comfortable with that word — ephemeral. It just means something that’s short-lived, temporary, meant to disappear. A morning fog is ephemeral. Snapchat stories are ephemeral. And, by design, so is everything you write inside a Docker container.
Here’s what’s actually happening under the hood. When you build a Docker image, you get a stack of read-only layers — think of them as frozen snapshots of a filesystem. When you run a container from that image, Docker adds one more layer on top: a thin writable layer, unique to that specific container. Every file you create, every log line you write, every temp file your app generates — it all lands in this writable layer.
The catch? That writable layer lives and dies with the container. Run docker container rm on that container, and the writable layer is gone. Along with it, any data you never explicitly saved somewhere else.
flowchart TB
subgraph Image["Docker Image (read-only layers)"]
L1["Layer 1: Base OS"]
L2["Layer 2: Dependencies"]
L3["Layer 3: App Code"]
end
W["Writable Layer<br/>(container-specific, ephemeral)"]
Image --> W
W -.->|"docker rm"| X["Gone 💨"]
This isn’t a bug or an oversight — it’s a deliberate design choice, and it’s core to how containers are meant to work.
Benefits: Ephemeral Nature is Great
Before you start thinking of this as a flaw, consider why it’s actually one of Docker’s best qualities.
Reproducibility. Since the writable layer is thrown away, every fresh container you spin up from the same image starts in exactly the same state. No “well it works on my machine because of some file I created three weeks ago” problem.
Effortless scaling. Need five identical replicas of your app for load balancing? Just start five containers from the same image. No manual syncing, no worrying that container #3 has drifted from container #1 because someone tweaked a config file directly inside it.
Clean testing and CI pipelines. Spin a container up, run your tests, tear it down. No cleanup script needed to reset state — the next run starts from a blank slate automatically.
Encourages immutable infrastructure. Instead of patching a running container to “fix” something, you rebuild the image and replace the container. This habit alone prevents a huge class of “it broke and nobody knows why” production incidents.
Easy debugging via disposability. If a container misbehaves, you don’t need to carefully untangle what state it’s in — just kill it and start a new one. This mindset shift (pets vs. cattle) is one of the biggest cultural wins containers brought to infrastructure.
The Problem: What Ephemeral Nature Costs You
Great, containers are disposable. But what about the stuff you actually need to keep?
Think about a database container. If every restart wiped its data, you’d have a pretty useless database. Same story for:
- User-uploaded files (profile pictures, documents)
- Application logs you need for auditing or debugging later
- Cached data that’s expensive to regenerate
- Configuration state that should persist across deployments
There’s also a second, less obvious problem: containers are isolated from each other by default, including their filesystems. If you have two containers that need to read or write the same data — say, a web server and a background worker both touching the same set of files — the ephemeral writable layer doesn’t help you here either, since it’s private to a single container.
Note
Restarting a container (docker container restart) doesn’t wipe the writable layer — that data survives a restart. It’s specifically removing the container (docker container rm, or letting an orchestrator recreate it) that destroys the writable layer.
This distinction trips people up a lot, especially when containers get recreated automatically during deployments, auto-scaling, or crash recovery — which happens far more often than people expect.
So we need a way to keep certain data outside the container’s ephemeral writable layer entirely — somewhere it survives no matter what happens to the container. That’s exactly what Docker’s storage options are for.
Quick Overview: Three Ways Docker Solves This
Docker gives you three mechanisms to persist or share data outside a container’s writable layer. Here’s the map before we dive into each one:
flowchart LR
C["Container"]
C -->|"bind mount"| H["Host Filesystem<br/>(any path you choose)"]
C -->|"volume"| D["Docker-Managed Storage<br/>(/var/lib/docker/volumes)"]
C -->|"tmpfs"| M["Host Memory (RAM)<br/>(never touches disk)"]
| Bind Mount | Volume | tmpfs | |
|---|---|---|---|
| Stored where | Any path on the host you specify | Docker-managed area on host disk | Host RAM only |
| Managed by | You (manual host paths) | Docker (via CLI/API) | Docker (in-memory) |
| Survives container removal | Yes | Yes | No |
| Portability | Low — depends on host path existing | High — works across environments | N/A — not persistent |
| Performance | Native host speed | Native, plus driver options | Fastest (RAM-speed), but non-persistent |
| Typical use case | Local dev, mounting source code | Databases, app data, production storage | Secrets, sensitive temp data |
A rough rule of thumb: use volumes by default, reach for bind mounts when you have a specific reason to tie a container to an exact host path (like live-reloading source code during development), and use tmpfs when you need blazing-fast, non-persistent storage that should never touch disk — like temporary secrets.
We’ll go through each of these in detail, starting with bind mounts since they’re the easiest to reason about.