Correct Docker Compose v2 Volumes Syntax: Named Volumes with Multiple Host Paths


2 views

When migrating to Docker Compose version 2 format, many developers encounter confusion around the new volumes top-level declaration. The key difference is that version 2 introduces a dedicated section for volume management, separating it from service definitions.

The AttributeError: 'list' object has no attribute 'items' occurs because the syntax being used attempts to define multiple host paths for a named volume using list notation, which isn't supported in the version 2 format.

Here's the proper way to define named volumes with host mappings in version 2:

version: '2.4'

services:
  db:
    image: postgres
    volumes:
      - database:/var/lib/postgres/data

  php:
    image: php-fpm:5.6
    volumes:
      - phpconf:/etc/php/conf.d

volumes:
  database:
    driver_opts:
      type: none
      device: ./Docker/Postgres/db
      o: bind,ro
  phpconf:
    driver_opts:
      type: none
      device: ./Docker/PHP-FPM/conf
      o: bind

The version 2 syntax requires:

  • Explicit volume declarations in the top-level volumes section
  • Use of driver_opts for host path mappings
  • Clear separation between service definitions and volume configurations

For complex scenarios with multiple path mappings, you'll need to create separate named volumes:

version: '2.4'

volumes:
  db_data:
    driver_opts:
      type: none
      device: ./Docker/Postgres/db
      o: bind,ro
  db_config:
    driver_opts:
      type: none
      device: ./Docker/Postgres/ini
      o: bind

1. Don't mix version 1 and version 2 syntax styles
2. Remember that volume names must be unique across the compose file
3. The driver_opts configuration is required for host path mappings

For production environments, consider using:

  • Volume drivers for persistent storage
  • Proper permission settings in driver_opts
  • Explicit volume naming for better maintainability

The Docker Compose version 2 file format introduced significant changes to volume management, particularly with the centralized volumes top-level key. Let's clarify the correct syntax for defining named volumes with host path mappings.

In version 2, named volumes must be declared under the top-level volumes key, while service-specific volume mounts reference these declarations. The syntax error in your example occurs because you're using a list structure where an object (key-value pairs) is required.

Here's the proper way to structure your docker-compose.yml:

version: '2.4'

services:
  db:
    image: postgres
    volumes:
      - database:/var/lib/postgres/data

  php:
    image: php-fpm:5.6
    volumes:
      - phpconf:/etc/php/conf.d
      - singledir:/etc/php/custom
      - completemap:/etc/service/conf.d

volumes:
  database:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ./Docker/Postgres/db
  phpconf:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ./Docker/PHP-FPM/conf
  singledir:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ./Docker/foo
  completemap:
    driver: local
    driver_opts:
      o: bind
      type: none
      device: ./Docker/bar
  • Each named volume must be defined as an object with driver configuration
  • The driver_opts specify the bind mount details for local paths
  • Multiple host paths cannot be mapped to a single named volume - each requires separate declaration

For more complex scenarios, you can specify additional volume options:

volumes:
  metrics:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs
      o: size=100m,uid=1000
  • Don't mix version 1 and version 2 syntax - stick to one format
  • Path mappings must be defined in driver_opts rather than directly under volume name
  • Ensure paths exist on host before starting containers