Docker Community Forums

Share and learn in the Docker community.

Docker-compose should support include instead of -f

If you have several docker docker-compose yml files that you want to refactor common parts from, there is currently only one way of referencing the code:

docker-compose-common.yml
folder1/docker-compose.yml
folder2/docker-compose.yml

The docker-compose files inside folders 1 and 2 assume the common.yml will be specified with -f.

The problem here is that for any docker-compose command that uses folders 1 or 2, the complete set of files must be given, and must therefore be known. Eg:

cd folder1
docker-compose -f ../docker-compose-common.yml -f docker-compose.yml up 
docker-compose -f ../docker-compose-common.yml -f docker-compose.yml down

etc. You have to remember this every time. And if you forget one of the files, docker-compose will not complain, the command will still operate, but only on the subset of services that is in the listed files.

This is unnecessarily error-prone. If the docker-compose yml supported an instruction like “include <other-docker-compose.yml file>” and recursively resolved those whenever it performs the inclusion, we would automatically get self-documenting yml file that clearly states which other files this one depends on. Also the above would allow all -f to be dropped:

cd folder1
docker-compose up

would automatically find the local docker-compose.yml, see that it includes a yml in …, load it, and continue.

Some years ago I would have been happy to see an include feature, but not today. It seems to be a good feature at first glance but it would have a side effect. People downloading one compose file would have to be careful and check if that file contains a reference to another compose file outside their project directory.

There are at least two similar features without editing the compose file:

  1. Using the standard docker-compose.yml as the main compose file docker-compose.override.yml would also be loaded if it exists to override the main file.
  2. You can set the environment variable COMPOSE_FILE to load different files by default.
    Example:
    export COMPOSE_FILE=../docker-compose.common.yml:docker-compose.yml
    
    You can set it globally in a bashrc or similar file or use a source ./activate.sh to load the variable locally. If you don’t use standard names, the users cannot acidentally run the project since docker-compose couldn’t find any compose file without setting the variable.

Other solution which I use is a simple shell script called run.sh in each project (when it is necessary) to start the project or sometimes I use docker-compose.sh which takes the same arguments as docker-compose so this can be misleading.

There is another not so popular feature, “profiles”. If you have some common services in the common compose file and each project contains additional services without modifying other services, you can use one compose file like this:

version: "3.7"

services:
  common1:
    image: httpd:2.4
    # common service

  common2:
    image: httpd:2.4
    # common service

  project1:
    image: httpd:2.4
    profiles: ["project1", "all"]

  project2:
    image: httpd:2.4
    profiles: ["project2", "all"]

And run like this:

# Run only common services
docker-compose up -d

# Run common and project1 services
docker-compose --profile project1 up -d

# Run multiple projects
docker-compose --profile project1 --profile project2 up -d

# Run all of the projects
docker-compose --profile all up -d

In this case I can use the YAML references like:

version: "3.7"

x-common-params: &common-params
  image: httpd:2.4

services:
  common1:
    <<: *common-params
    # common service

  common2:
    <<: *common-params
    # common service

  project1:
    <<: *common-params
    profiles: ["project1", "all"]

  project2:
    <<: *common-params
    profiles: ["project2", "all"]  

If it is not enough for me, I use Ansible with it’s template engine, Jinja2.

I hope one of these solutions can help you if you can’t get the include feature in the future releases.

Furthermore you can use envsubst from the gettext package to allow a simple on the fly templating to make your compose file flexible:

envsubst < /path/to/compose | docker-compose -f - ${command}

Of yourse this makes most sense in a bash script that passes all arguments passed to the script to docker-compose -f $@ or inside a Makefile.

Like Akos wrote: Ansible is good idea if you need powerfull templating. It allows condition rendering of blocks and loops,