dockerdocker-compose

Overriding Compose profiles with 'extends' instead of appending them


I have recently tried to combine Docker Compose profiles with the extends: feature to create a slightly modified service B based on the main service A.

   a_service:
    profiles: [a]
    some_settings: ...
    ports:
    - 8080:8080

   b_service:
    profiles: [b]
    extends: a_service
    ports:
    - 8090:8080

How I would have liked it to work:

  1. Service B extends service A and makes a change to something like a port / auth service etc
  2. Service A has profile 'a' / the default profile
  3. Service B has profile 'b'
  4. Specify profile 'a' with docker compose and only deploy A

The issue: Service B seems to inherit the profile from A and appends its own profile instead of overriding it (as is the case with other variables). It now has ['a', 'b'] profiles. There doesn't seem to be a way to deploy A but not B, since any profile assigned to A gets passed to B as well.

I have tested this with both the array syntax [a] as well as the list syntax "- a".

Any easy way to use inheritance with profiles? Or would you recommend another approach that doesn't require copying over the entire service?


Solution

  • The behavior you describe is explicit in the Compose Specification definition of extends::

    Merging service definitions

    Sequences: Items are combined together into a new sequence. The order of elements is preserved with the referenced items coming first and main items after.

    That is, in the Compose file you show, b's profiles: and ports: are appended to a's; there is no way to replace them.

    Instead of trying to override settings from some base service, you can make this work if you define a new artificial service, giving it a profiles: value you'll never use (compare Is there any way to disable a service in docker-compose.yml). In that base service, define all of the values that you won't need to override. Both services you're planning to run need to extends: this base service. Each will add a new profiles: value to the list, so it will run in both the profile you want and the "never-use-this" profile.

    version: "4.0" # Works with all versions of Docker Compose tool version 2.x
    services:
      base:
        profiles: [do-not-use]
        build: .
        image: registry.example.com/image:${IMAGE_TAG:-latest}
      a:
        extends: {service: base}
        profiles: [a]
        ports: ["8080:8080"]
      b:
        extends: {service: base}
        profiles: [b]
        ports: ["8090:8080"]
    

    If you only have a couple of settings you want to reuse, then YAML anchors are a popular technique to have the same content in multiple places in the file. This is more useful if you have a complex build: or environment: block that you want to reuse, rather than wholesale reusing a previous service definition with some tweaks.

    version: "2.4" # Works with all versions of Docker Compose tool, both 1.x and 2.x
    services:
      a:
        build: &build-a
          context: .
          dockerfile: subdir/Dockerfile
          args: [foo, bar, baz]
        profiles: [a]
        ...
      b:
        build: *build-a
        profiles: [b]
        ...