Docker Compose merge arrays for YAML aliases and anchors

Viewed 3388

I have the following broken docker-compose file

version: '3.4'

x-vols1: &vols-1
    - /home/:/home/

x-vols2: &vols-2
    - /tmp/:/tmp/

services:
    app1:
        container_name: app1
        image: app1
        volumes:
            <<: *vols-1
    app2:
        container_name: app2
        image: app2
        volumes:
            <<: *vols-1
            <<: *vols-2

This fails with the following error

$ docker-compose -f test.yaml config
ERROR: yaml.constructor.ConstructorError: while constructing a mapping
  in "./test.yaml", line 14, column 13
expected a mapping for merging, but found scalar
  in "./test.yaml", line 4, column 7

Question 1: How can I merge arrays in docker-compose? The syntax that I am trying to use is the one for merging dicts

Question 2: If there is no way to merge arrays, is there a workaround?

Use case: I have multiple services, some of them map some volumes, others map other volumes, others map all volumes. I would like to not repeat myself.

Thank you!

3 Answers

The Yaml merge syntax is for merging mappings, not for arrays. For more on that, see this issue. However, if you are just adding single volumes, you don't need to merge anything. Just insert the alias as an array entry:

version: '3.4'

x-vols1: &vols-1
    "/home/:/home/"

x-vols2: &vols-2
    "/tmp/:/tmp/"

services:
    app1:
        container_name: app1
        image: app1
        volumes:
            - *vols-1
    app2:
        container_name: app2
        image: app2
        volumes:
            - *vols-1
            - *vols-2

The desired behavior can be achieved by using multiple docker-compose files, one for each volume. Note that the anchors and aliases are not required, but keeping them in to align with the question.

base.yaml

version: '3.4'

services:
    app1:
        container_name: app1
        image: app1
    app2:
        container_name: app2
        image: app2

vol1.yaml

version: '3.4'

x-vols1: &vols-1
    volumes:
        - /home/:/home/

services:
    app1:
        container_name: app1
        image: app1
        <<: *vols-1
    app2:
        container_name: app2
        image: app2
        <<: *vols-1

vol2.yaml

version: '3.4'

x-vols2: &vols-2
    volumes:
        - /tmp/:/tmp/

services:
    app2:
        container_name: app2
        image: app2
        <<: *vols-2

Verify as

$ docker-compose -f base.yaml -f vol1.yaml -f vol2.yaml config

Result

services:
  app1:
    container_name: app1
    image: app1
    volumes:
    - /home:/home:rw
  app2:
    container_name: app2
    image: app2
    volumes:
    - /home:/home:rw
    - /tmp:/tmp:rw
version: '3.4'

Additional documentation https://docs.docker.com/compose/extends/

This is a slight variation on the currently accepted answer, which doesn't require you to change your docker-compose command line to include other files:

docker-compose.yml

version: '3.9'

services:
    app1:
        container_name: app1
        image: app1
        extends:
            file: common-volumes.yml
            service: app1
    app2:
        container_name: app2
        image: app2
        extends:
            file: common-volumes.yml
            service: app2
        volumes:
            - /tmp/:/tmp/

common-volumes.yml

x-vols1: &vols-1
    volumes:
        - /home/:/home/

services:
    app1:
        <<: *vols-1
    app2:
        <<: *vols-1

To view the resulting config:

docker compose config
Related