Skip to main content
Volumes provide persistent storage for containers. Uncloud supports named Docker volumes, bind mounts, and tmpfs mounts.

Volume types

Named volumes (volume)

Named volumes are managed by Docker and persist across container restarts:
services:
  db:
    image: postgres:16
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:
References: pkg/api/volume.go:16-26

Bind mounts (bind)

Bind mounts map a host path to a container path:
services:
  web:
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - /var/log/nginx:/var/log/nginx
You can use long syntax for additional options:
services:
  app:
    image: myapp
    volumes:
      - type: bind
        source: ./config
        target: /etc/app
        read_only: true
        bind:
          create_host_path: true
          propagation: rprivate
Bind options:
  • create_host_path - Create host directory if it doesn’t exist
  • propagation - Mount propagation mode (rprivate, private, rshared, shared, rslave, slave)
References: pkg/api/volume.go:39-48, pkg/client/compose/service.go:295-312

Tmpfs mounts (tmpfs)

Tmpfs mounts are stored in memory and cleared when the container stops:
services:
  app:
    image: myapp
    volumes:
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 10485760  # 10MB in bytes
          mode: 1777      # Octal file permissions
References: pkg/api/volume.go:35, pkg/client/compose/service.go:355-368

Volume declaration

Declare volumes at the top level to use them in services:
services:
  app:
    image: myapp
    volumes:
      - app-data:/data

volumes:
  app-data:      # Simple declaration
  app-cache:     # Docker creates these with default settings

Volume options

Configure volume creation:
volumes:
  app-data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /path/on/host
    labels:
      env: production
      app: myapp
Volume options:
  • driver - Volume driver (default: local)
  • driver_opts - Driver-specific options
  • labels - Metadata labels
  • name - Override volume name
  • external - Use existing volume
References: pkg/api/volume.go:50-64, pkg/client/compose/service.go:314-343

External volumes

Reference volumes created outside Compose:
services:
  app:
    image: myapp
    volumes:
      - shared-data:/data

volumes:
  shared-data:
    external: true  # Must already exist
External volumes must be created manually before deployment using uc volume create.
References: pkg/client/compose/deploy.go:168-192

Volume name aliasing

Use different names in compose.yaml vs Docker:
services:
  app:
    image: myapp
    volumes:
      - data-alias:/data

volumes:
  data-alias:
    name: production-data  # Actual Docker volume name

Volume mounting

Short syntax

The short syntax is concise:
volumes:
  - volume-name:/container/path
  - /host/path:/container/path
  - /host/path:/container/path:ro
  - volume-name:/container/path:rw
Format: [source:]target[:mode]
  • source - Volume name or host path
  • target - Container path (must be absolute)
  • mode - ro (read-only) or rw (read-write, default)

Long syntax

The long syntax provides more control:
volumes:
  - type: volume
    source: app-data
    target: /data
    read_only: false
    volume:
      nocopy: true      # Don't copy data from container to volume
      subpath: app/logs # Mount subdirectory of volume

  - type: bind
    source: ./config
    target: /etc/app
    read_only: true
    bind:
      create_host_path: true
      propagation: rprivate
References: pkg/api/volume.go:198-220

Cross-machine considerations

Volume placement

Named volumes are machine-local in Uncloud. When services use volumes, Uncloud ensures they run on the same machine as the volume.
services:
  app:
    image: myapp
    volumes:
      - app-data:/data
    deploy:
      replicas: 3  # All 3 replicas on same machine

volumes:
  app-data:
All replicas of a service using the same named volume will be placed on the same machine to share the volume.

Multiple volumes

When multiple services use different volumes, Uncloud tries to optimize placement:
services:
  app1:
    image: app1
    volumes:
      - data1:/data
    deploy:
      replicas: 2

  app2:
    image: app2
    volumes:
      - data2:/data
    deploy:
      replicas: 2

volumes:
  data1:
  data2:
Uncloud can place data1 on one machine and data2 on another, distributing the services. References: test/e2e/compose_deploy_test.go:206-388

Shared volumes

Multiple services can share the same volume, which constrains them to the same machine:
services:
  writer:
    image: writer
    volumes:
      - shared-data:/data

  reader:
    image: reader
    volumes:
      - shared-data:/data:ro  # Read-only

volumes:
  shared-data:
Both writer and reader will be placed on the same machine.

Global services with volumes

Global services (one container per machine) automatically create volumes on each machine:
services:
  node-exporter:
    image: prom/node-exporter
    volumes:
      - metrics:/metrics
    deploy:
      mode: global  # One per machine

volumes:
  metrics:
Uncloud creates a metrics volume on each machine for that machine’s container. References: test/e2e/compose_deploy_test.go:562-601

Bind mount patterns

Configuration files

Mount individual config files:
services:
  nginx:
    image: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl/cert.pem:/etc/nginx/ssl/cert.pem:ro
      - ./ssl/key.pem:/etc/nginx/ssl/key.pem:ro

Application code

Mount application code for development:
services:
  app:
    image: node:20
    command: npm run dev
    volumes:
      - ./src:/app/src:ro
      - ./package.json:/app/package.json:ro

Shared host paths

Share host directories across services:
services:
  processor:
    image: processor
    volumes:
      - /data/input:/input:ro
      - /data/output:/output

  uploader:
    image: uploader
    volumes:
      - /data/output:/data:ro

Volume drivers

Uncloud supports the local volume driver:
volumes:
  data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /mnt/data
Only the local driver is currently supported. Network storage drivers (NFS, etc.) are not yet supported.
References: pkg/api/volume.go:24-26

Complete example

services:
  web:
    image: nginx:alpine
    volumes:
      # Named volume for content
      - web-content:/usr/share/nginx/html:ro
      
      # Bind mount for configuration
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      
      # Tmpfs for temporary files
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 10485760
    deploy:
      replicas: 2

  app:
    image: myapp
    volumes:
      # Long syntax with options
      - type: volume
        source: app-data
        target: /data
        volume:
          nocopy: true
      
      # Logs volume shared with processor
      - logs:/var/log/app
    deploy:
      replicas: 3

  log-processor:
    image: log-processor
    volumes:
      # Read-only access to logs
      - logs:/logs:ro
    deploy:
      replicas: 1

  metrics:
    image: prom/node-exporter
    volumes:
      # Bind mount host metrics
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
    deploy:
      mode: global  # One per machine

volumes:
  web-content:
    labels:
      app: web
  app-data:
    driver: local
  logs:
    name: application-logs

Build docs developers (and LLMs) love