docker-compose template

Run it

Execute this template like so:

cookiecutter https://quico.space/Quico/py-cookiecutter-templates.git --directory 'docker-compose'

Cookiecutter interactively prompts you for the following info, here with example answers:

project_slug [dir-name]: grafana
service [grafana]:
component_list [grafana]: grafana,nginx
Select build:
1 - no
2 - yes
Choose from 1, 2 [1]: 

Done, directory structure and files for your next docker-compose project are ready for you to hit the ground running.

Explanation and terminology

Your four answers translate as follows into rendered files.

  1. The project_slug is used only as directory name. A container named vault may be fine but the project directory name hashicorpvault might be more descriptive.

    .
    └── hashicorpvault                                        <--- Here
        ├── build-context
        │   ├── docker-data
        │   │   └── .gitkeep
        │   ├── Dockerfile
        ...
    
  2. The service variable by default copies your project_slug answer. It's a style decision whether you leave it at that and just hit Enter. The service name will come up in rendered compose.yaml at purely cosmetic locations such as the networks: key, container_name: and /opt/docker-data volume mount presets, here with ftp as the service name:

    services:
        mysql:
            image: "mysql:${MYSQL_VERSION}"
            container_name: "ftp-mysql-${CONTEXT}"            <---
            networks:
                ftp-default:                                  <---
            ...
            volumes:
                # - /opt/docker-data/ftp-mysql-${CONTEXT}/... <---
                # - /opt/docker-data/ftp-mysql-${CONTEXT}/... <---
                # - /opt/docker-data/ftp-mysql-${CONTEXT}/... <---
        ...
    
  3. Treat component_list as the list of Docker images that make up your service. Each docker-compose project forms a service - see above - that consists of either a single or multiple components. They're your services:, your container, volume, variable names etc.:

    services:
        grafana:                                              <---
            image: "grafana:${GRAFANA_VERSION}"               <---
            container_name: "grafana-grafana-${CONTEXT}"      <---
            ...
            environment:
                # GRAFANA_USER: ${GRAFANA_USER}               <---
                # GRAFANA_PASSWORD: ${GRAFANA_PASSWORD}       <---
        ...
    
  4. Prompt build is a yes-no question. Cookiecutter will create a README.md file with copy-pastable Docker Compose commands pre-filled. If you answer yes to this prompt README.md will contain an example paragraph that explains the build process along the lines of:

    docker compose ... --profile 'build' build
    

    Whereas by answering no (or just hitting <Enter> to accept the default of no) no such paragraph will be added to the example Markdown file. Build instructions are only really needed if you need to locally build a derivative image.

Also check out the Caveats section at the end to learn what this template does not do well.

Result

Multi-component service

Above example of a multi-component (two in this case) grafana service will give you this directory structure:

.
└── grafana
    ├── build-context
    │   ├── grafana
    │   │   ├── docker-data
    │   │   │   └── .gitkeep
    │   │   ├── Dockerfile
    │   │   └── extras
    │   │       └── .gitkeep
    │   └── nginx
    │       ├── docker-data
    │       │   └── .gitkeep
    │       ├── Dockerfile
    │       └── extras
    │           └── .gitkeep
    ├── common-settings.yaml
    ├── compose.override.yaml
    ├── compose.yaml
    ├── env
    │   └── fqdn_context.env.example
    └── README.md

Check out file contents over in the examples/grafana subdir.

Single-component service

With an alternative single-component hashicorpvault service the result may look like this:

.
└── hashicorpvault
    ├── build-context
    │   ├── docker-data
    │   │   └── .gitkeep
    │   ├── Dockerfile
    │   └── extras
    │       └── .gitkeep
    ├── common-settings.yaml
    ├── compose.override.yaml
    ├── compose.yaml
    ├── env
    │   └── fqdn_context.env.example
    └── README.md

Check out file contents over in the examples/hashicorpvault subdir.

Caveats

Consider Cookiecutter's project directory and rendered files a starting point. It won't do everything perfectly.

Imagine if you will a service that consists of Infinispan among other things. In Docker Hub's content-addressable image store Infinispan's location is at infinispan/server so you obviously want that exact string with a forward slash to show up in your compose.yaml as the image: key's value, same with your Dockerfile. The image: key's value comes from what you enter in Cookiecutter's component_list prompt. Component strings are then used to also pre-fill the volumes: key.

This will cause obvious issues (but the image: key is kinda correct):

services:
    infinispan/server:
        image: "infinispan/server:${INFINISPAN/SERVER_VERSION}"
        container_name: "cacheman-infinispan/server-${CONTEXT}"

This won't cause issues (but you'll have to then go in and manually change the image: key to use infinispan/server):

services:
    infinispan:
        image: "infinispan:${INFINISPAN_VERSION}"
        container_name: "cacheman-infinispan-${CONTEXT}"

You're going to want to keep it simple and go with option 2.