Skip to main content

Docker Volumes

Uncloud uses native Docker volumes for persistent storage. Volumes provide a way to store data that survives container restarts and updates.

Volume Types

Uncloud supports three types of volumes:

Named Volumes

Managed by Docker, stored in Docker’s volume directory:
services:
  database:
    image: postgres:15
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
    driver: local
Named volumes are the recommended approach for most stateful services.

Bind Mounts

Mount a host directory directly into the container:
services:
  web:
    image: nginx:latest
    volumes:
      - type: bind
        source: /opt/website
        target: /usr/share/nginx/html
Use bind mounts when you need direct access to files from the host.

Tmpfs Mounts

Store data in memory (not persistent):
services:
  cache:
    image: redis:latest
    volumes:
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100m
Useful for temporary data that doesn’t need to survive restarts.
Named volumes and bind mounts provide persistence. Tmpfs mounts do not persist data across container restarts.

Volume Lifecycle

Creating Volumes

Volumes can be created:
  1. Automatically during deployment:
volumes:
  appdata:
    driver: local
Uncloud creates the volume if it doesn’t exist when deploying the service.
  1. Manually before deployment:
# Create volume on a specific machine
uc volume create myvolume --machine machine-name

Listing Volumes

View all volumes across the cluster:
uc volume ls
Output shows:
  • Volume name
  • Driver (usually local)
  • Which machine the volume is on

Removing Volumes

Delete a volume:
uc volume rm volume-name
Removing a volume permanently deletes all data in it. This operation cannot be undone.

Volume Placement

Volumes exist on a specific machine and cannot be automatically moved or shared between machines.

Single Machine Constraint

When you use a volume:
services:
  database:
    image: postgres:15
    volumes:
      - dbdata:/var/lib/postgresql/data
    deploy:
      replicas: 1

volumes:
  dbdata:
    driver: local
The container must run on the same machine as the volume. Uncloud automatically:
  1. Creates the volume on the target machine
  2. Ensures the container is placed on that machine
  3. Prevents the container from being scheduled elsewhere
Services with volumes typically use replicas: 1 since the volume only exists on one machine.

Explicit Placement

You can specify which machine should host the volume:
services:
  database:
    image: postgres:15
    volumes:
      - dbdata:/var/lib/postgresql/data
    deploy:
      placement:
        constraints:
          - machine.name == storage-server

volumes:
  dbdata:
    driver: local
This ensures the volume is created on storage-server.

Stateful Services

Single Replica Stateful Services

For databases and other stateful applications:
services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    deploy:
      replicas: 1
      update_config:
        order: stop-first

volumes:
  pgdata:
    driver: local
Key points:
  • Use replicas: 1 since volumes are machine-local
  • Set update_config.order: stop-first to prevent data corruption
  • The old container stops before the new one starts, using the same volume
Uncloud defaults to stop-first update order for services with volumes to prevent concurrent access to the volume.

Read-Only Volumes

Mount volumes as read-only to prevent accidental modifications:
services:
  web:
    image: nginx:latest
    volumes:
      - config:/etc/nginx/conf.d:ro

volumes:
  config:
    driver: local
The :ro suffix makes the mount read-only inside the container.

Volume Drivers

Uncloud supports different volume drivers for various storage backends.

Local Driver (Default)

The local driver stores data in a directory on the machine:
volumes:
  mydata:
    driver: local
Data is stored at /var/lib/docker/volumes/mydata/_data on the machine.

Custom Driver Options

Configure driver-specific options:
volumes:
  mydata:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.10,rw
      device: ":/path/to/nfs/share"
This uses NFS for shared storage across machines.
NFS and other network storage drivers allow multiple machines to access the same volume, enabling stateful services to move between machines.

Multi-Machine Storage Strategies

Since Docker volumes are machine-local, you need additional strategies for multi-machine deployments.

Replicated Stateless Services

Avoid volumes entirely for stateless services:
services:
  api:
    image: myapp/api
    deploy:
      replicas: 5
    # No volumes - completely stateless
Store state in:
  • External database services
  • Object storage (S3, MinIO)
  • Caching services (Redis, Memcached)

Database Per Machine

Run a database replica on each machine:
services:
  postgres:
    image: postgres:15
    deploy:
      mode: global
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
    driver: local
Configure replication within PostgreSQL to sync data between instances.

Network Storage

Use NFS, GlusterFS, or Ceph for shared volumes:
volumes:
  shared:
    driver: local
    driver_opts:
      type: nfs
      o: addr=nfs-server.internal,rw
      device: ":/exports/shared"
Multiple containers across machines can access the same data.
Network storage adds complexity and potential performance bottlenecks. Use it only when necessary.

Object Storage

For static files, use object storage like S3:
services:
  web:
    image: myapp/web
    environment:
      S3_BUCKET: my-app-assets
      AWS_ACCESS_KEY_ID: ...
      AWS_SECRET_ACCESS_KEY: ...
Store uploads and media in S3 instead of local volumes.

Volume Management

Volume Labels

Add metadata to volumes:
volumes:
  appdata:
    driver: local
    labels:
      app: myapp
      environment: production
Filter volumes by labels:
uc volume ls --filter label=app=myapp

Volume Inspection

View detailed volume information:
uc volume inspect volume-name
Shows:
  • Volume name and driver
  • Mount point on the host
  • Labels and options
  • Which machine it’s on

Volume Pruning

Remove unused volumes:
# Remove volumes not used by any container
uc volume prune
Volume pruning permanently deletes data. Use with caution, especially in production.

Backup and Restore

Uncloud doesn’t provide built-in backup tools, but you can use standard Docker and Linux tools.

Backup Strategies

Manual Backup

Create a backup container:
uc run --rm -v myvolume:/data -v /backup:/backup \
  alpine tar czf /backup/myvolume-backup.tar.gz -C /data .
This creates a compressed archive of the volume.

Automated Backups

Run a backup service:
services:
  backup:
    image: myapp/backup
    volumes:
      - pgdata:/source:ro
      - backup:/backup
    environment:
      BACKUP_SCHEDULE: "0 2 * * *"
    deploy:
      mode: global

volumes:
  pgdata:
    external: true
  backup:
    driver: local
The backup service reads from application volumes and writes to a backup volume.

Database-Specific Tools

Use database native tools:
# Backup PostgreSQL
uc exec database pg_dump -U postgres mydb > backup.sql

# Restore PostgreSQL
cat backup.sql | uc exec -i database psql -U postgres mydb

Restore Process

  1. Create new volume:
uc volume create myvolume-restored
  1. Restore data:
uc run --rm -v myvolume-restored:/data -v /backup:/backup \
  alpine tar xzf /backup/myvolume-backup.tar.gz -C /data
  1. Update service to use restored volume:
volumes:
  mydata:
    name: myvolume-restored
    external: true
Always test your backup and restore procedures before relying on them in production.

Storage Performance

Local Driver Performance

The default local driver uses the host filesystem:
  • Performance matches the underlying disk (SSD, HDD, etc.)
  • No additional overhead beyond Docker’s volume management
  • Best performance for most use cases

Network Storage Performance

NFS and similar network storage:
  • Higher latency than local storage
  • Throughput limited by network bandwidth
  • Suitable for shared configuration files, not high-IOPS databases

Storage Optimization

For better performance:
  1. Use local SSDs for database volumes
  2. Avoid network storage for high-throughput workloads
  3. Use tmpfs for temporary data that doesn’t need persistence
  4. Tune filesystem options based on your workload

Best Practices

Do:

  • Use named volumes for persistent data
  • Set update_config.order: stop-first for stateful services
  • Regularly backup important volumes
  • Test restore procedures before you need them
  • Use read-only mounts when containers don’t need write access
  • Document which machine volumes are on

Don’t:

  • Don’t rely on container filesystem for persistent data
  • Don’t run multiple replicas with the same volume
  • Don’t use tmpfs for data that needs to persist
  • Don’t assume volumes will automatically move between machines
  • Don’t forget to backup before major operations
Containers are ephemeral. Always use volumes for data you need to keep.

Further Reading

Services

Learn about deploying stateful services

Machines

Understand machine placement and roles

Volume Commands

Creating and managing volumes with the CLI

Docker Volumes

Official Docker volumes documentation

Build docs developers (and LLMs) love