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