Back to BlogDocker Swarm · cluster · docker

Persistent Storage in Multi-Node Swarm: Plugins and Cross-Node Volumes

2025-12-29

Containers are ephemeral by design—when a container stops, its writable layer disappears. For stateless applications, this is perfect. However, databases, file stores, and other stateful applications need persistent storage that survives container restarts and can follow tasks as they're rescheduled across nodes. This article explores persistent storage solutions for Docker Swarm, including volume plugins, cross-node storage systems, and patterns for managing stateful workloads in distributed environments.

Understanding Storage Challenges in Swarm

The Container Storage Problem

Containers have three storage layers:

Image Layers: Read-only layers from the container image Container Layer: Thin writable layer created when container starts Volumes: Persistent storage mounted into containers

When a container stops, the container layer is deleted. Any data written there is lost. Volumes provide persistence, but vanilla Docker volumes are local to a single host.

Multi-Node Storage Challenges

In a Swarm cluster, tasks can be scheduled on any node. This creates storage challenges:

Task Mobility: When a task moves to a different node, it needs access to the same data

Data Consistency: Multiple replicas may try to write to the same data simultaneously

Performance: Network-attached storage introduces latency compared to local disks

Availability: Storage systems become critical dependencies

Solving these challenges requires storage solutions that work across the cluster.

Docker Volume Basics

Creating Local Volumes

Create a standard local volume:

docker volume create mydata

This creates a volume on the local node only. Services using this volume must be pinned to this specific node.

Mounting Volumes in Services

Attach volumes to services:

docker service create \
  --name database \
  --mount type=volume,source=mydata,target=/var/lib/mysql \
  mysql:8

The volume is mounted at /var/lib/mysql inside containers.

Volume Mount Options

Specify additional mount options:

docker service create \
  --name app \
  --mount type=volume,source=appdata,target=/data,readonly \
  app-image

The readonly option prevents containers from writing to the volume.

Volume Inspection

View volume details:

docker volume inspect mydata

Shows volume name, driver, mountpoint, and options.

Local Volume Limitations

Local volumes have critical limitations in Swarm:

  • Exist only on one node
  • Tasks using local volumes must be constrained to that node
  • No automatic replication
  • Node failure means data loss unless separately backed up
  • Cannot support multi-replica services that need shared data

These limitations make local volumes unsuitable for most distributed scenarios.

Volume Drivers and Plugins

What Are Volume Drivers?

Volume drivers extend Docker's storage capabilities. While the default driver creates local volumes, plugin drivers enable:

  • Network-attached storage
  • Cloud storage integration
  • Distributed filesystems
  • Storage replication and redundancy

Installing Volume Plugins

Volume plugins are typically distributed as Docker plugins:

# Example: Installing REX-Ray plugin
docker plugin install rexray/ebs \
  EBS_ACCESSKEY=XXX \
  EBS_SECRETKEY=YYY \
  --grant-all-permissions

The --grant-all-permissions grants necessary privileges to the plugin.

Listing Installed Plugins

View available plugins:

docker plugin ls

Shows plugin names, status (enabled/disabled), and capabilities.

Plugin Management

Enable or disable plugins:

# Disable plugin
docker plugin disable rexray/ebs

# Enable plugin
docker plugin enable rexray/ebs

# Remove plugin
docker plugin rm rexray/ebs

Plugins can only be removed when no volumes are using them.

Network-Attached Storage with NFS

NFS Volume Driver

NFS (Network File System) is a widely-supported network storage protocol. Docker includes built-in NFS support through volume driver options.

Setting Up NFS Server

First, configure an NFS server (example on Ubuntu):

# Install NFS server
apt-get install nfs-kernel-server

# Create export directory
mkdir -p /srv/nfs/swarm-data
chown nobody:nogroup /srv/nfs/swarm-data
chmod 777 /srv/nfs/swarm-data

# Configure exports
echo "/srv/nfs/swarm-data *(rw,sync,no_subtree_check,no_root_squash)" >> /etc/exports

# Apply configuration
exportfs -ra
systemctl restart nfs-kernel-server

Creating NFS Volumes

Create a volume using NFS:

docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=nfs-server.example.com,rw \
  --opt device=:/srv/nfs/swarm-data \
  nfs-volume

This creates a volume that mounts the NFS export.

Using NFS Volumes in Services

Deploy services with NFS volumes:

docker service create \
  --name webapp \
  --replicas 3 \
  --mount type=volume,source=nfs-volume,target=/app/data \
  webapp-image

All three replicas share the same NFS mount, accessing identical data.

NFS Volume Driver Options

Customize NFS mounts with options:

docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=nfs-server,vers=4.1,rw \
  --opt device=:/export/path \
  nfs-vol-v4

The vers=4.1 specifies NFS version 4.1.

NFS Performance Considerations

NFS introduces network latency:

Sequential Reads/Writes: Reasonable performance for bulk operations Random I/O: Significantly slower than local disks Metadata Operations: Creating/deleting files is slower Multiple Clients: Performance decreases with many concurrent clients

For high-performance requirements, consider alternatives.

NFS High Availability

Make NFS highly available:

Multiple NFS Servers: Deploy redundant NFS servers with shared storage backend Load Balancing: Use DNS or load balancers to distribute clients Failover Clustering: Implement automatic failover between NFS servers

Without HA, NFS server failure stops all dependent services.

REX-Ray for Cloud Storage

What is REX-Ray?

REX-Ray is a volume plugin that integrates Docker with cloud storage services:

  • AWS EBS (Elastic Block Store)
  • Google Persistent Disks
  • Azure Managed Disks
  • OpenStack Cinder
  • VirtualBox storage

REX-Ray handles volume creation, attachment, and detachment automatically.

Installing REX-Ray for AWS EBS

Install REX-Ray plugin for AWS:

docker plugin install rexray/ebs \
  EBS_ACCESSKEY=your-access-key \
  EBS_SECRETKEY=your-secret-key \
  EBS_REGION=us-east-1 \
  --grant-all-permissions

Creating EBS Volumes

Create a volume backed by AWS EBS:

docker volume create \
  --driver rexray/ebs \
  --opt size=10 \
  --opt volumetype=gp3 \
  ebs-volume

This creates a 10GB GP3 EBS volume in AWS.

Using REX-Ray Volumes

Deploy services with REX-Ray volumes:

docker service create \
  --name postgres \
  --replicas 1 \
  --mount type=volume,source=ebs-volume,target=/var/lib/postgresql/data \
  postgres:14

When the task is scheduled, REX-Ray:

  1. Attaches the EBS volume to the node running the task
  2. Mounts the volume inside the container
  3. If the task moves, REX-Ray detaches from old node and attaches to new node

REX-Ray Limitations

Single Attachment: EBS volumes can only attach to one node at a time Replica Count: Services using REX-Ray must have --replicas 1 Attachment Time: Moving tasks incurs EBS attach/detach delays (10-30 seconds) Regional Constraint: Volumes and nodes must be in the same AWS region/zone

These limitations make REX-Ray suitable for single-instance stateful services, not multi-replica workloads.

REX-Ray Configuration File

Configure REX-Ray via config file for more control:

# /etc/rexray/config.yml
libstorage:
  service: ebs
ebs:
  accessKey: YOUR_ACCESS_KEY
  secretKey: YOUR_SECRET_KEY
  region: us-east-1

Then install without inline credentials:

docker plugin install rexray/ebs --grant-all-permissions

GlusterFS for Distributed Storage

What is GlusterFS?

GlusterFS is a distributed network filesystem that aggregates storage from multiple servers into a single namespace. It provides:

  • Scalability across many storage servers
  • Replication for data redundancy
  • High availability through redundancy
  • POSIX-compliant filesystem interface

GlusterFS Architecture

GlusterFS consists of:

Bricks: Storage directories on individual servers Volumes: Logical volumes composed of multiple bricks Gluster Clients: Nodes that mount GlusterFS volumes

Setting Up GlusterFS Cluster

Install GlusterFS on storage nodes:

# On each storage node
apt-get install glusterfs-server
systemctl start glusterd
systemctl enable glusterd

# Peer the nodes (run on one node)
gluster peer probe storage-node-2
gluster peer probe storage-node-3

# Create a replicated volume
gluster volume create swarm-data replica 3 \
  storage-node-1:/data/brick1 \
  storage-node-2:/data/brick1 \
  storage-node-3:/data/brick1 \
  force

# Start the volume
gluster volume start swarm-data

This creates a 3-way replicated volume.

Mounting GlusterFS Volumes

Create Docker volumes backed by GlusterFS:

docker volume create \
  --driver local \
  --opt type=glusterfs \
  --opt o=addr=storage-node-1,backup-volfile-servers=storage-node-2:storage-node-3 \
  --opt device=:/swarm-data \
  gluster-volume

Using GlusterFS Volumes in Services

Deploy services with GlusterFS:

docker service create \
  --name webapp \
  --replicas 5 \
  --mount type=volume,source=gluster-volume,target=/app/uploads \
  webapp-image

All five replicas share the same GlusterFS volume with replicated data.

GlusterFS Volume Types

GlusterFS supports different volume types:

Distributed: Files distributed across bricks (no redundancy) Replicated: Files replicated across bricks (high redundancy) Distributed-Replicated: Combination of both

For Swarm, use replicated or distributed-replicated volumes for data safety.

GlusterFS Performance

GlusterFS performance characteristics:

Reads: Scale with number of storage nodes Writes: Limited by replication factor (writes go to all replicas) Small Files: Metadata overhead impacts performance Large Files: Better performance for bulk operations

Tune GlusterFS parameters for your workload patterns.

Flocker for Container Data Management

What is Flocker?

Flocker is a data volume manager for containerized applications. It provides:

  • Volume migration between hosts
  • ZFS-based storage with snapshots
  • Integration with cloud storage backends
  • Dataset-centric storage management

Note: Flocker is no longer actively maintained, but similar concepts apply to modern alternatives.

Alternative: Portworx

Portworx is a modern container-native storage platform:

  • Software-defined storage across cluster nodes
  • Volume replication and high availability
  • Snapshots and clones
  • Disaster recovery features
  • Cloud-native integration

Install Portworx on Swarm nodes and use its volume driver for persistent storage across the cluster.

Convoy for Volume Snapshots

What is Convoy?

Convoy is a Docker volume plugin supporting:

  • Volume snapshots
  • Volume backups
  • Device Mapper thin provisioning
  • NFS backend support

Creating Snapshot-Enabled Volumes

With Convoy installed:

docker volume create \
  --driver convoy \
  --opt backup=incremental \
  convoy-volume

Taking Volume Snapshots

Create snapshots of volumes:

# Using Convoy CLI
convoy snapshot create \
  --name snapshot-1 \
  convoy-volume

Snapshots capture point-in-time states for backup or rollback.

Ceph RBD for Block Storage

What is Ceph?

Ceph is a distributed storage system providing:

  • Object storage
  • Block storage (RBD)
  • Filesystem (CephFS)

For Docker, Ceph RBD provides block devices that can be mounted as volumes.

Ceph RBD Volume Driver

Use Ceph RBD with Docker:

docker volume create \
  --driver rbd \
  --opt size=10 \
  rbd-volume

Ceph RBD volumes can be attached to containers, providing persistent block storage backed by Ceph's distributed architecture.

Ceph Replication

Ceph automatically replicates data across cluster nodes. Configure replication level:

  • 2x replication: Data stored on two nodes
  • 3x replication: Data stored on three nodes (recommended)

Higher replication increases availability and durability.

Volume Sharing Patterns

Single-Writer Multiple-Reader

One task writes, many tasks read:

# Writer service
docker service create \
  --name writer \
  --replicas 1 \
  --mount type=volume,source=shared-data,target=/data \
  writer-app

# Reader services
docker service create \
  --name reader \
  --replicas 5 \
  --mount type=volume,source=shared-data,target=/data,readonly \
  reader-app

Readers mount the volume as read-only, preventing accidental writes.

Exclusive Single-Writer

Only one task writes, enforced at scheduling:

docker service create \
  --name database \
  --replicas 1 \
  --constraint 'node.labels.storage-role==primary' \
  --mount type=volume,source=db-data,target=/var/lib/postgresql/data \
  postgres

Constraint ensures only one replica exists, preventing write conflicts.

Coordinated Multi-Writer

Multiple tasks write with coordination:

Use application-level locking or coordination:

  • Database locking mechanisms
  • Distributed locks (e.g., etcd, Consul)
  • Application-level synchronization

The storage system must support concurrent writes safely (like GlusterFS or shared database storage).

Local Volume Constraints

Pinning Services to Nodes

When using local volumes, pin services to specific nodes:

# Label node with storage
docker node update --label-add storage=local-db node-1

# Create service constrained to that node
docker service create \
  --name database \
  --replicas 1 \
  --constraint 'node.labels.storage==local-db' \
  --mount type=volume,source=local-db-vol,target=/var/lib/postgresql/data \
  postgres

The service always runs on node-1 where the local volume exists.

Multiple Local Volumes

Deploy multiple instances with separate local volumes:

# Label nodes
docker node update --label-add db-instance=1 node-1
docker node update --label-add db-instance=2 node-2

# Create constrained services
docker service create \
  --name db-instance-1 \
  --replicas 1 \
  --constraint 'node.labels.db-instance==1' \
  --mount type=volume,source=db-vol-1,target=/var/lib/postgresql/data \
  postgres

docker service create \
  --name db-instance-2 \
  --replicas 1 \
  --constraint 'node.labels.db-instance==2' \
  --mount type=volume,source=db-vol-2,target=/var/lib/postgresql/data \
  postgres

Each instance has its own local volume on its designated node.

Bind Mounts for Host Paths

Creating Bind Mounts

Mount host directories into containers:

docker service create \
  --name app \
  --mount type=bind,source=/host/path,target=/container/path \
  app-image

This directly mounts /host/path from the node into the container.

Bind Mount Use Cases

Bind mounts are useful for:

  • Accessing host configuration files
  • Sharing data between host and container
  • Development environments with code on host

Bind Mount Limitations

Node-Specific: Bind mounts reference local host paths Must Exist: Path must exist on the host No Portability: Services using bind mounts must be constrained to specific nodes

For production, prefer volume plugins over bind mounts.

Bind Mount with Node Constraints

Ensure path exists on target nodes:

# Label nodes with shared mount
docker node update --label-add has-shared-mount=true node-1
docker node update --label-add has-shared-mount=true node-2

# Create service
docker service create \
  --name app \
  --constraint 'node.labels.has-shared-mount==true' \
  --mount type=bind,source=/mnt/shared,target=/data \
  app-image

Only schedules on nodes labeled as having the bind mount path.

Tmpfs Mounts for Memory Storage

Creating Tmpfs Mounts

Store data in memory:

docker service create \
  --name cache \
  --mount type=tmpfs,target=/cache,tmpfs-size=1g \
  cache-app

Tmpfs mounts are stored in RAM, providing extremely fast access but no persistence.

Tmpfs Use Cases

Use tmpfs for:

  • Temporary caches
  • Session data
  • Build artifacts
  • Any ephemeral data

Tmpfs Limitations

Non-Persistent: Data lost when container stops Memory Consumption: Uses node's RAM Size Limits: Constrained by available memory

Backup and Recovery Strategies

Volume Backup Approaches

Snapshot-Based: Use storage system snapshots (Ceph, Portworx) File-Based: Copy files from mounted volumes Database-Specific: Use database backup tools (pg_dump, mysqldump)

Backup Service Pattern

Create a backup service:

docker service create \
  --name backup \
  --mode global \
  --mount type=volume,source=db-data,target=/backup-source,readonly \
  --mount type=volume,source=backup-dest,target=/backup-dest \
  backup-image

Backup service reads from source volumes and writes to backup destination.

Scheduled Backups

Use cron or systemd timers to schedule backups:

# Example backup script
#!/bin/bash
BACKUP_DATE=$(date +%Y%m%d-%H%M%S)
docker run --rm \
  -v db-data:/source:ro \
  -v /backups:/dest \
  backup-tool \
  backup /source /dest/backup-$BACKUP_DATE

Disaster Recovery

Plan for volume recovery:

  1. Maintain backup of volume data
  2. Document volume creation procedures
  3. Test restore procedures regularly
  4. Keep backup retention policy

Performance Optimization

Choosing the Right Storage Backend

Match storage to workload:

High IOPS Databases: Local SSDs or high-performance cloud block storage Shared File Storage: NFS or GlusterFS for concurrent access Large Media Files: Object storage with CDN Logs and Metrics: Fast sequential write storage

Caching Strategies

Implement caching layers:

Application Cache: Redis or Memcached for hot data CDN: CloudFront or Cloudflare for static assets Local Caching: tmpfs for frequently accessed data

Reduce storage system load through caching.

Storage Tiering

Use different storage tiers:

Hot Data: Fast SSDs or local storage Warm Data: Network storage (NFS, GlusterFS) Cold Data: Object storage (S3, Minio)

Move data between tiers based on access patterns.

Best Practices for Swarm Storage

Volume Naming Conventions

Use descriptive volume names:

# Good names
docker volume create prod-database-data
docker volume create staging-uploads

# Avoid
docker volume create vol1
docker volume create data

Label Volumes

Add metadata labels:

docker volume create \
  --label environment=production \
  --label application=webapp \
  --label created-by=admin \
  prod-webapp-data

Labels help organize and filter volumes.

Regular Backups

Implement automated backup schedules:

  • Daily backups for critical data
  • Weekly backups for less critical data
  • Retain multiple backup generations
  • Store backups off-cluster

Test Restore Procedures

Regularly test volume restoration:

  1. Restore from backup to test environment
  2. Verify data integrity
  3. Document restore process
  4. Time the restore procedure

Monitor Storage Usage

Track storage metrics:

  • Volume utilization
  • IOPS and throughput
  • Latency percentiles
  • Error rates

Set alerts for approaching capacity limits.

Plan for Growth

Anticipate storage growth:

  • Monitor growth trends
  • Provision ahead of needs
  • Plan for storage system expansion
  • Consider storage lifecycle management

Security Considerations

Secure persistent storage:

  • Encrypt volumes at rest
  • Encrypt network storage traffic
  • Implement access controls
  • Audit storage access

Documentation

Document storage architecture:

  • Which services use which volumes
  • Storage backend configurations
  • Backup and recovery procedures
  • Troubleshooting guides

Persistent storage in Docker Swarm requires careful planning and the right tools. Local volumes work for development but lack the mobility needed in production clusters. Volume plugins like REX-Ray, distributed filesystems like GlusterFS, and network storage solutions like NFS provide the cross-node accessibility stateful applications require. By understanding the trade-offs between different storage solutions—considering factors like performance, availability, consistency, and operational complexity—you can architect storage systems that meet your applications' durability and accessibility requirements while operating efficiently across your Swarm cluster.

We use cookies to improve your experience and analyse site traffic. See our Privacy Policy.