Docker Volume Plugin Architecture, Explained From Zero
How Docker lets outside storage systems plug themselves into containers — and how a single write on disk quietly becomes a request to a completely different piece of software.
Imagine you check into a hotel that doesn’t own its own laundry room. Instead, the front desk has a list of trusted laundry services in town, each with a matching pickup process. You hand your clothes to the front desk, the desk calls the right laundry service using a shared checklist both sides agreed on in advance, and a few hours later your clothes come back — cleaned by a company you never spoke to directly. You only ever dealt with the front desk. Docker’s storage system works in almost exactly this way. Docker itself is not very good at talking to exotic storage systems — network file shares, cloud disks, distributed storage clusters — so instead of trying to understand all of them, it defines a shared checklist (an API) and lets outside “storage services” register themselves as plugins. This tutorial walks through that entire arrangement — the Volume Plugin Architecture — piece by piece, assuming no prior knowledge of Docker internals.
1What Is a Docker Volume, and Why Do We Need Plugins?
Before plugins make sense, volumes themselves need to make sense.
Containers forget everything by default
A Docker container is built from an image, and everything a running container writes to its own filesystem lives in a thin, temporary write layer on top of that image. The moment the container is removed, that write layer is destroyed along with it. This is intentional — containers are meant to be disposable, rebuildable, and identical every time they start. But it creates an obvious problem: a database container that loses its data every time it restarts is useless. Something outside the container’s disposable layer has to hold onto the data.
Think of a container like a hotel room that gets fully reset and re-cleaned between guests — nothing you leave on the bed survives to the next guest. A volume is like a personal storage locker down in the basement that survives no matter how many times the room upstairs is reset.
Enter the volume
A Docker volume is storage that lives outside the container’s writable layer, managed by Docker itself, and can be attached to (or detached from) one or more containers. When a container writes to a path that has a volume mounted on it, that data goes into the volume instead of into the disposable container layer, so it survives container removal, container recreation, and even movement to a different host in some setups.
Why “just write to disk” isn’t enough
The simplest kind of volume, called a local volume, just stores data in a folder on the Docker host’s own disk. That works fine for a single machine running a single workload. But real infrastructure rarely stays that simple. A company might want its container data to live on a network-attached storage array, replicate across three data centers, live on a cloud provider’s managed block storage, or be encrypted by a dedicated storage appliance. Docker cannot reasonably know how to speak the private language of every storage vendor on the planet — and it shouldn’t have to. This is exactly the gap that volume plugins were built to close.
Host-disk storage
Data sits in a directory on the Docker host itself, managed by Docker’s built-in driver. No plugin needed.
Externally-managed storage
Data is created, mounted, and removed by a separate piece of software that speaks Docker’s plugin protocol.
The plugin’s real name
Inside Docker, a volume plugin is registered under the label “driver” — you’re really choosing a driver when you choose a plugin.
The actual system
The real storage technology behind the plugin — a SAN, a cloud disk service, a distributed filesystem, and so on.
Every volume in Docker — even the boring local one — is technically handled “through a driver.” The local driver just happens to already live inside Docker’s engine, so it never needs a plugin. Volume plugins exist for every driver that Docker does not already know how to run.
2The Building Blocks of the Volume Plugin Architecture
Four pieces cooperate to make external storage feel native to Docker.
The Volume Plugin Architecture is not one single component — it is a small set of cooperating parts, each with one clear job. Understanding these parts individually makes the whole system much easier to reason about later, when we talk about how a request actually travels through them.
1. The Docker Engine (dockerd)
The core Docker daemon. It never talks to storage hardware directly for plugin-backed volumes — instead it acts as a translator and traffic director, converting ordinary Docker commands into plugin API calls.
2. The Volume Subsystem
A component inside the engine that keeps a registry of every driver Docker currently knows about — the built-in local driver plus any registered plugins — and routes each volume operation to the correct one based on which driver that volume was created with.
3. The Plugin (Volume Driver)
A separate running process — sometimes a regular OS-level binary, sometimes itself a container — that implements a fixed set of operations Docker expects every volume driver to support, and translates those operations into whatever the real storage backend actually needs.
4. The Plugin Discovery Mechanism
The part of Docker responsible for finding out which plugins exist on the host at all, so it knows where to send requests for a driver name like “rexray” or “netapp”.
How Docker finds a plugin’s “address”
For a plugin to be usable, Docker has to know two things about it: its name, and how to reach it. Historically, this happened through small files placed in specific directories on the host — a .spec file containing a network address, or a .json file describing how to launch the plugin, or a Unix domain socket file that the plugin process is already listening on. Docker checks these locations, in a fixed order, whenever a driver name it doesn’t already have registered is requested. Once found, Docker keeps that plugin’s connection details cached in memory so it doesn’t have to repeat the discovery search on every single volume operation.
flowchart TB
A["docker volume create
--driver=mystorage"] --> B{"Docker Engine"}
B --> C["Volume Subsystem
(driver registry)"]
C -->|"driver name known?"| D{"Lookup"}
D -->|"Yes, cached"| E["Reuse existing
plugin connection"]
D -->|"No, unknown"| F["Plugin Discovery:
check spec / json / socket files"]
F --> G["Connect to Plugin Process
over HTTP-over-socket"]
E --> H["Volume Driver Plugin"]
G --> H
H --> I["Real Storage Backend
(SAN, cloud disk, NFS, etc.)"]
Two generations of plugins
Docker has shipped two different mechanisms for running plugins over the years. The original, sometimes called “legacy” or “V1” plugins, are ordinary background processes that the host’s own operating system starts and manages, listening on a Unix socket or TCP port. The newer generation, called “managed plugins” or “plugin V2,” packages the plugin itself as a special kind of container, installed and controlled with Docker’s own plugin commands, and supervised by the Docker Engine the same way it supervises containers. Managed plugins are easier to distribute, version, and enable or disable, because Docker treats the whole plugin lifecycle the same way it treats image and container lifecycles.
| Aspect | Legacy (V1) Plugin | Managed (V2) Plugin |
|---|---|---|
| Runs as | Native OS process | Specially packaged container |
| Installed via | Manual OS-level setup | Docker’s own plugin management commands |
| Lifecycle managed by | The host OS / init system | The Docker Engine itself |
| Distribution | OS packages, manual copy | Pushed and pulled like images, from a registry |
| Isolation | Runs with host-level access | Runs in a constrained, permissioned sandbox |
3How the Engine and a Plugin Actually Talk
Underneath everything, it’s a shared vocabulary sent as small JSON messages.
Once Docker has located a plugin, the two sides communicate using a simple, fixed vocabulary of remote procedure calls, sent as small JSON messages over HTTP — usually over a Unix domain socket, which is a local, file-based communication channel rather than a network connection, though a network address is also supported. Every volume plugin, no matter which vendor wrote it or what real storage system sits behind it, must understand the same handful of “endpoints,” each named after the action it performs.
VolumeDriver.Create
Asks the plugin to allocate a new volume with a given name and any driver-specific options.
VolumeDriver.Mount
Asks the plugin to make an existing volume available on the host filesystem and return the path.
VolumeDriver.Path
Asks the plugin where on the host filesystem a given volume currently lives.
VolumeDriver.Unmount
Tells the plugin a container is done using the volume for now, so it can be safely detached.
VolumeDriver.Remove
Asks the plugin to permanently delete a volume and free its underlying storage.
VolumeDriver.List, .Get
Lets Docker ask the plugin which volumes it currently manages, or details about one specific volume.
VolumeDriver.Capabilities
Tells Docker whether this plugin’s volumes are safe to share across multiple hosts at once (“global”) or only usable on one host (“local”).
Because this vocabulary is fixed and shared, the Docker Engine never needs to know anything specific about, say, a particular cloud provider’s disk API. It only ever needs to know how to send a “Create” or “Mount” request and interpret the plugin’s JSON response. All of the vendor-specific translation work — authenticating to a cloud account, formatting a filesystem, attaching a network share — happens entirely inside the plugin, hidden from Docker’s view.
It’s similar to how a universal remote control works with televisions from different brands. The remote only knows a small, fixed set of buttons — power, volume, channel. It has no idea how a Sony TV’s internal circuitry differs from a Samsung’s. Each TV’s own receiver translates the same button press into whatever its internal hardware actually needs.
People sometimes assume the plugin directly mounts storage inside the container. It doesn’t. The plugin prepares and mounts the storage onto a path on the host; Docker’s own container runtime is what then bind-mounts that host path into the container’s filesystem namespace.
4The Full Lifecycle of a Plugin-Backed Volume
From “doesn’t exist” to “gone forever,” a volume passes through a predictable sequence of states.
A volume’s life follows a fairly strict order of events, and understanding this order clears up a lot of confusion about when data actually gets created, attached, or destroyed.
Creation
A user or an orchestrator asks Docker to create a volume with a specific driver name and options. Docker forwards this as a Create call to the matching plugin, which allocates storage on the backend (for example, provisioning a new disk) but does not attach anything to a container yet.
Container Start / Mount Request
When a container that references this volume is started, Docker sends a Mount call. The plugin makes the storage available at a path on the host — for a network share, this is often the moment it actually connects to the remote system — and reports that path back.
Bind Into the Container
Docker’s container runtime bind-mounts the host path the plugin returned into the container’s own filesystem view, at the path the user specified in their volume mount configuration.
Active Use
The container reads and writes through the mounted path as if it were ordinary local disk. All of this activity actually flows to whatever real backend the plugin is managing.
Container Stop / Unmount
When the container stops, Docker sends an Unmount call. Many plugins track a reference count here — if two containers were using the same volume, the storage stays mounted until the last one lets go.
Removal
Only when a Remove call is explicitly issued does the plugin actually delete the underlying storage. Simply stopping and removing a container does not destroy a plugin-backed volume — this separation is what makes volumes useful for persistence in the first place.
sequenceDiagram
participant User
participant Engine as Docker Engine
participant Plugin as Volume Plugin
participant Backend as Storage Backend
User->>Engine: docker volume create
Engine->>Plugin: VolumeDriver.Create
Plugin->>Backend: Provision storage
Backend-->>Plugin: Storage ready
Plugin-->>Engine: OK
User->>Engine: docker run (uses volume)
Engine->>Plugin: VolumeDriver.Mount
Plugin->>Backend: Attach / connect
Backend-->>Plugin: Mounted at host path
Plugin-->>Engine: Host path
Engine->>Engine: Bind path into container
User->>Engine: docker stop
Engine->>Plugin: VolumeDriver.Unmount
User->>Engine: docker volume rm
Engine->>Plugin: VolumeDriver.Remove
Plugin->>Backend: Delete storage
5Local vs Global Scope, and Why It Matters for Clusters
Not all storage behaves the same way when containers move between machines.
When Docker runs across multiple machines — as it does in a Swarm cluster — a critical question arises: if a container using a particular volume gets rescheduled from Host A to Host B, will that volume’s data follow it? The answer depends entirely on the plugin’s declared scope, reported through the Capabilities endpoint described earlier.
Local Scope
- Volume only exists meaningfully on the host where it was created
- Typical for plugins backed by the host’s own physical disk
- A container rescheduled to another host will get a brand-new, empty volume of the same name there
Global Scope
- Volume is backed by shared or networked storage reachable from every host
- The orchestrator can safely move the container to any node and reattach the same data
- Typical for SAN, cloud block storage, or distributed filesystem plugins
Choosing a locally-scoped plugin for a workload that needs to move freely between machines is one of the most common causes of “my data disappeared after a failover” incidents. The fix isn’t a Docker bug fix — it’s picking a plugin whose backend can genuinely be reached from every node in the cluster.
Single-host vs multi-host in practice
On a single developer laptop, scope rarely matters — everything lives on one machine anyway. In production clusters, though, scope becomes one of the first questions to answer when selecting a storage plugin, right alongside performance and cost, because it directly determines whether the architecture can tolerate a node failure without losing data availability.
6Categories of Storage Behind Volume Plugins
Different plugins wrap fundamentally different kinds of storage technology.
Volume plugins aren’t a single category of software — they’re a thin, standardized wrapper placed in front of very different underlying storage technologies. Grouping them by the kind of backend they wrap makes their trade-offs much easier to compare.
Block storage plugins
Block storage presents itself to a container as if it were a raw, unformatted disk — the kind of storage cloud providers sell as attachable “volumes” for virtual machines. Plugins in this category typically provision a disk, attach it to the host, format it if needed, and mount the resulting filesystem. Block storage tends to offer strong performance and per-container isolation, but it’s usually attachable to only one host at a time, pushing these plugins toward the “local scope” category discussed earlier unless the backend specifically supports multi-attach.
File storage (network filesystem) plugins
File storage plugins wrap systems like NFS or SMB/CIFS shares, or distributed filesystems purpose-built for this role. Because the underlying protocol is designed for many clients to read and write the same files simultaneously, these plugins are naturally suited to global scope and multi-container sharing, at some cost in raw throughput compared to block storage.
Object and distributed storage plugins
A smaller category of plugins presents object storage — normally accessed through an API rather than a filesystem — as if it were a mountable volume, or wraps a purpose-built distributed storage cluster designed from the ground up for container workloads. These plugins often add their own replication, snapshotting, and encryption features on top of the raw storage layer.
Best for
Databases and workloads needing high, consistent I/O performance on a single node.
Best for
Workloads where many containers need to read and write the same shared files at once.
Best for
Large-scale, resilient storage across many nodes, often with built-in replication.
Best for
Development, caching layers, and anything that doesn’t need to survive a host failure.
7Advantages, Disadvantages, and Trade-offs
Plugins solve a real problem, but the flexibility they add is not free.
Advantages
- Decouples Docker’s core engine from every possible storage vendor’s private API
- Lets storage vendors ship and update their own integration on their own release schedule
- Enables advanced features — snapshots, encryption, replication — without bloating Docker itself
- Managed (V2) plugins install, version, and remove as cleanly as container images
- A single fixed API means the same orchestration tooling works across wildly different backends
Disadvantages / Trade-offs
- Every plugin call adds a network or socket round-trip that a purely local volume never needs
- Plugin quality and reliability vary enormously between vendors
- Debugging spans two systems — Docker’s logs and the plugin’s own logs — which slows troubleshooting
- A crashed or unresponsive plugin can leave containers stuck waiting to start
- Legacy (V1) plugins typically run with broad host-level access, widening the security surface
Outsourcing your laundry to the hotel’s partner service is more flexible than doing it yourself — but now your clean shirt depends on a courier, a third-party company, and a phone line all working correctly, instead of just you and a washing machine down the hall.
8Security Considerations
A plugin sits in a position of real trust, and that trust needs to be earned deliberately.
Privilege and trust boundaries
A volume plugin usually needs meaningful privileges to do its job — access to storage credentials, the ability to mount filesystems on the host, and in many cases network access to a storage backend that may hold data for many different tenants or applications. This makes the plugin itself a high-value target: a compromised or poorly written plugin can potentially read or corrupt data belonging to any container that uses it, not just one.
Legacy plugin exposure
Because legacy (V1) plugins are ordinary host processes, they typically run with the same broad privileges as the Docker daemon itself, with no additional sandboxing enforced by Docker. Managed (V2) plugins improve on this by running inside a container-like sandbox with an explicit, declared set of permissions — network access, mount capabilities, device access — that must be granted at install time, making the plugin’s access footprint visible and auditable up front.
Storage secrets
API keys or credentials the plugin uses to reach its backend should be stored and rotated carefully, never hardcoded into images.
Encrypt in transit
Traffic between the plugin and a remote storage backend should be encrypted, especially for network filesystem and object storage plugins.
Encrypt stored data
Whether encryption at rest is available and enabled depends entirely on the backend and plugin — Docker itself has no opinion on this.
Tenant separation
Multi-tenant storage backends need the plugin to enforce that one container’s volume can never be mounted or read by another’s.
Treating a volume plugin as “just storage config” and skipping the same vendor-trust review applied to any other piece of privileged software. A volume plugin effectively has a standing relationship with the Docker daemon and, through it, with every container on the host.
9Monitoring, Logging, and Metrics
A silent plugin failure often looks like a mysterious container start-up hang.
Where things go wrong, and where to look
Because a volume operation crosses two systems, useful monitoring has to cover both sides: Docker’s own engine logs, which record whether a plugin call was sent and what response came back, and the plugin’s own logs, which record what actually happened when it tried to reach its storage backend. When a container appears stuck starting, the very first place to look is whether its Mount call to the plugin ever completed.
Engine-side signals
Docker’s daemon logs show plugin registration events, connection failures, and timeouts on individual API calls — the first layer of visibility into whether the plugin is even reachable.
Plugin-side signals
Most well-built plugins emit their own logs (for managed V2 plugins, viewable the same way as container logs) describing backend connection attempts, authentication results, and storage provisioning outcomes.
Backend-side signals
The storage system itself — a SAN controller, a cloud storage service, a distributed cluster — usually has its own metrics for capacity, latency, and error rates, which are invisible to Docker entirely but critical to the full picture.
Metrics worth tracking
Beyond raw availability, teams running plugin-backed storage at scale typically track mount latency (how long a Mount call takes to complete), call failure rates per plugin, volume creation success rates, and — on the backend — capacity utilization and I/O latency, since a plugin can be perfectly healthy while the storage behind it is running out of room or overloaded.
10Design Patterns and Anti-Patterns
The same architecture can be used well or badly — here’s the difference.
Pattern: one driver, many named volumes
A healthy pattern is registering a single plugin driver per backend technology, then creating many distinctly-named volumes through it — one per application or per data set — rather than reusing a single shared volume across unrelated workloads. This keeps blast radius small: a problem with one application’s data doesn’t risk another’s.
Pattern: explicit scope awareness in orchestration
Well-designed deployments explicitly account for a plugin’s local-versus-global scope when defining placement constraints, pinning workloads that need locally-scoped storage to specific nodes rather than letting the orchestrator reschedule them freely and silently lose data continuity.
Problem
Using a locally-scoped volume plugin for a stateful service running under an orchestrator that freely reschedules containers across nodes.
Why It’s Harmful
When the container moves to a new node, it silently receives an empty volume with the same name instead of its previous data — with no error raised, since the operation technically “succeeds.”
Correct Approach
Either pin the workload to its original node with placement constraints, or switch to a plugin whose backend is reachable from every node in the cluster.
Problem
Installing an unmaintained or unaudited third-party plugin purely because it appeared first in a search result.
Why It’s Harmful
Given the elevated privileges most volume plugins require, an abandoned or poorly written plugin becomes a long-lived, unpatched risk sitting between every container and its persistent data.
Correct Approach
Prefer actively maintained plugins from the storage vendor itself or a well-established open-source project, and review update history before adopting one in production.
11Best Practices and Common Mistakes
Small operational habits that separate stable plugin deployments from fragile ones.
Name volumes deliberately
Use clear, application-specific volume names rather than relying on Docker’s auto-generated identifiers, so ownership is obvious months later.
Test failover before production
Deliberately reschedule a stateful test container between nodes and confirm data actually follows it, rather than assuming the plugin’s scope claims are correct.
Prefer managed (V2) plugins
Where available, choose managed plugins for their sandboxing, easier upgrades, and consistent lifecycle management.
Version-pin plugin installs
Pin a specific plugin version in deployment automation rather than always installing “latest,” to avoid unexpected behavior changes.
Ignoring Remove semantics
Assuming a volume disappears automatically when its last container is deleted — it does not, and this leads to either orphaned storage costs or, worse, accidental early deletion via automation that misunderstands the lifecycle.
Mixing local and global assumptions
Writing deployment scripts that assume all volumes behave the same way, when in fact different plugins in the same environment can have entirely different scope guarantees.
12Real-World and Industry Examples
The abstract architecture becomes concrete once you see who actually builds on it.
A wide range of storage vendors and open-source projects have built volume plugins on top of this exact architecture, each translating Docker’s fixed vocabulary into their own backend’s language.
Cloud block storage integrations
Several plugins exist purely to provision and attach a major cloud provider’s block storage service as Docker volumes, letting containerized workloads use the same durable, snapshot-capable disks that virtual machines on the same cloud already rely on.
Enterprise storage array integrations
Established enterprise storage vendors publish plugins that let existing SAN and NAS hardware, already deployed in a company’s data center, serve as the backing storage for containerized applications without buying new hardware.
Software-defined and distributed storage projects
Several open-source and commercial software-defined storage platforms — designed specifically to pool disks across a cluster of ordinary servers into one resilient storage layer — ship Docker volume plugins as one of several supported integration points, alongside native Kubernetes support.
Simple network-share plugins
A large number of lightweight, community-maintained plugins do nothing more than mount an existing NFS or SMB share as a Docker volume — a modest but extremely common real-world use case for teams that already have a network file server.
Kubernetes uses a different, newer standard called the Container Storage Interface (CSI), which serves a similar purpose but is not the same protocol as Docker’s volume plugin API. Some vendors ship both a Docker volume plugin and a separate CSI driver so their storage works consistently whether it’s used under Docker Swarm or Kubernetes.
13Frequently Asked Questions
No. The built-in local driver handles ordinary host-disk volumes with no plugin involved. Plugins are only needed when you want storage backed by something Docker doesn’t already understand natively.
Containers that are already running and mounted usually keep working, since the mount itself lives at the OS level. However, new Mount, Unmount, or Remove calls for that driver will fail until the plugin process is restored, which can block new containers from starting.
It depends entirely on the plugin and its backend. Some plugins and backends fully support concurrent multi-container access; others enforce single-writer access and will reject or serialize simultaneous mounts.
No. They serve a similar conceptual purpose but use different protocols — Docker’s own Volume Plugin API versus Kubernetes’ Container Storage Interface — even though many vendors offer both.
Mostly for compatibility — some older or highly specialized storage integrations were never repackaged as managed plugins. For new deployments, managed plugins are generally the safer and easier-to-operate default where one is available.
No. Removing a container only triggers an Unmount call, not a Remove call. The volume and its underlying data persist until it is explicitly removed on its own.
14Summary and Key Takeaways
The Volume Plugin Architecture is Docker’s answer to a simple but unavoidable problem: no single piece of software can natively understand every storage system a business might want to use. Rather than trying, Docker defines a small, fixed set of operations — create, mount, path, unmount, remove, list, and capabilities — and lets independent plugins translate those operations into whatever a real storage backend actually requires. The Docker Engine never needs vendor-specific knowledge; it only needs to speak this one shared vocabulary, whether the plugin behind it manages a cloud disk, a network file share, or a distributed storage cluster.
Key Takeaways
- Volumes exist to survive containers. Anything written to a container’s own writable layer disappears when the container is removed; volumes live outside that layer.
- A plugin is really a “driver.” Choosing a plugin means choosing a driver name that Docker routes volume operations to.
- Communication is a fixed API. Every plugin implements the same small set of JSON-over-socket operations, regardless of its backend.
- Create, Mount, and Remove are three separate events. Data is not destroyed just because a container stops or is deleted — only an explicit Remove call does that.
- Scope determines cluster behavior. Local-scope plugins don’t follow containers between hosts; global-scope plugins do, which is critical for stateful workloads in a cluster.
- Privilege demands scrutiny. A plugin sits in a trusted position with access to storage credentials and host mount capabilities, so its source and maintenance quality matter.
- Managed (V2) plugins are the modern default — sandboxed, versioned, and lifecycle-managed the same way Docker already manages images and containers.