Welcome to Decomposer, the Docker-Compose Configuration Manager. As a Makefile include, it easily integrates into your Makefile-based or shell-script build and deployment tools.
This Makefile facilitates dynamically generating docker-compose.yml
files from reusable service declarations. You might want to do this to test your application on different deployment targets or environments without duplicating common service definitions. Or, more easily re-use service definitions from one project to the next. Other use-cases might be: having different stacks for development, unit testing, integration and QA testing, or generating production builds.
To generate a docker-compose.yml
file and an .env
file, decomposer looks in the environment variables, STACK_SERVICES
and ENV_INCLUDES
, to generate a list of file includes. You can easily switch between stacks using the activate
and deactivate
commands. You must first set the STACK
environment variable to the name of the stack you want to activate. Using the stack name, additional files are automatically discovered and included in the docker-compose.yml
and .env
files.
For lazy typers, decomposer aliases docker compose
commands. Rather than typing docker compose
commands directly, you will use make
followed by the docker-compose command you want to run, e.g. make up
, make down
, make exec
, etc. See the Aliases and Custom Commands section for additional commands not provided by docker compose
.
In addition to wrapping docker compose
, decomposer smooths-out some rough-edges in the docker-compose
cli. For example, automatically passing the --rm
flag to docker compose run
, bringing up applications in detached mode by default, or cleanly removing a task by implicitly calling docker-compose rm --force --stop
.
The command wrappers also facilitate extending your development environment with ad hoc commands in your main Makefile. For example, you can run make TASK=shell
to run a shell in the active stack, or make TASK=php8-apache RUN_CMD='php -i'
to run php -i
in the PHP 8 Apache container.
Decomposer need not be adopted by an entire team because it does not break classic docker-compose
usage. For those team members that do not need to switch between stacks, you can commit fully-generated docker-compose.yml
files to your repository, and they can use docker-compose
as they always have.
Gain deeper insights into Decomposer on folder structure, environment variables, and configuration files:
- see docs/Orientation
Makefile global includes are typically installed in /usr/local/include
. To install Decomposer as a global include, clone the repository and run make install
with admin permissions. You can override the install location with INSTALL_LOCATION
. But really, "installation" is just copying the decomposer.mk
file to a location where you can include it in your top-level Makefile.
Docker Compose definitions are decomposed into smaller, modular files for better maintainability and flexibility. When you set the STACK
environment variable and run make activate
, a docker-compose.yml
and a .env
file are generated. The docker/
directory contains service definitions in the form of docker-compose service declaration fragments, using docker compose config
to unify the fragments.
The stack is defined by the STACK_SERVICES
environment variable, each word will automatically have the .yml
extension appended to it. The docker-compose.yml
file is generated by pattern-matching on the stack-name in the project-root directory, any corresponding .yml
files for each service declared in the stack, and any additional files specified in the STACK_INCLUDES
environment variable. You can include any arbitrary file by adding it to the STACK_INCLUDES
environment variable, as long as it is in the docker/
directory.
Then, each of your services in the docker/
directory will start with a service
stanza, and one service definition.
services:
<my-service>:
image: <my-image>
build:
args:
LOGIN_UID: ${LOGIN_UID:-1000}
LOGIN_GID: ${LOGIN_GID:-1000}
FROM_IMAGE: ${FROM_IMAGE:-php:8-apache}
# ...
This takes advantage of the fact that docker compose
can take multiple configuration files, and compose them. Rather than passing a list of files on each invocation, we use the config
command to pre-generate a single docker-compose.yml
file. This is why you must activate
a stack before running docker compose
commands.
The .env
file is concatenated from files discovered by pattern-matching on the stack-name in the project-root directory, any corresponding .env
files for each service declared in the stack, and any additional files specified in the ENV_INCLUDES
environment variable.
The Makefile utilizes the following environment variables:
STACK
: Defines the active stack.STACK_SERVICES
: Specifies the list of services within the stack.ENV_INCLUDES
: Additional files used to generate the.env
file.COMPOSER_PROJECT_NAME
: Determines the project's network name if you don't want to use the default.DK_CMP_OPTS
: Options passed todocker-compose
commands.TASK
: Specifies a docker-compose service, used to run ad hoc commands.RUN_CMD
: Command to execute in the specified service.CMD_ARGS
: Additional options forRUN_CMD
.
Here are some common usage examples:
STACK=lamp make up
: Bring up the LAMP stack.STACK=lamp TASK=shell make run
: Run a shell in the LAMP stack.make TASK=mysqld top
: View the top processes in the MySQL service.make config CMD_ARGS=--services
: Review the generateddocker-compose.yml
.make build TASK=php8-apache DK_CMP_OPTS='--no-cache'
: Build the PHP 8 Apache image with no cache.make exec TASK=php8-apache RUN_CMD='php -i'
: Executephp -i
in the PHP 8 Apache container.make rund TASK=shell RUN_CMD='php' CMD_ARGS="-r 'phpinfo();'"
: Run PHP withphpinfo()
in the shell container.
If you are having trouble defining commands, you can set the environment variable DEBUG
to be non-empty, and the Makefile will print the constructed command.
Any docker-compose
command can be run with make
by prefixing the command with make
. For example, docker compose up
becomes make up
. In addition to the docker-compose
commands, the Makefile provides some additional commands:
make services
: List the services in the active stack. Alias ofdocker compose config --services
.make config
: Review the generateddocker-compose.yml
file. Alias ofdocker compose config
.make up
: Bring up the active stack, and detach. Alias ofdocker compose up -d
.make down
: Stop and remove the active stack. When a TASK is specified, alias ofdocker-compose rm --force --stop
.make run
: Run a command in the specified service (TASK
) and remove it when stopped. Alias ofdocker compose run --rm
.make rund
: Likerun
, but runs detached (like in background). Alias ofdocker compose run -d
.make orphans
: Remove orphaned containers. Alias ofdocker compose down --remove-orphans
.
NOTE: some newer
docker-compose
commands are not yet aliased. You can still run them withmake
by prefixing the compose command withdkc-
, e.g.make dkc-images
, ormake dkc-copy
.
Bonus for lazy-typers: recommended aliases to add to your shell profile:
alias dk='docker'
alias dkc='docker compose'
alias dki='docker image'
alias dkl='docker ps -a --format '\''table {{if .ID}}{{slice .ID 0 4}}{{end}}\t{{.Names}}\t{{if .Status}}{{slice .Status 0 2}}{{end}}\t{{.Image}}\t{{.Command}}'\'''
alias dkll='dkl --no-trunc'
alias dkr='docker run --rm'
alias dke='docker exec -it'
If you're new to Docker Compose and are not familiar with project names, networks, volumes, tasks, and common Docker Compose commands, see our [docs/Compose Quick-start](docs/Compose Quick-start.md) for some sign-posts to start your journey.
For more detailed explanations and references, feel free to explore Docker Compose documentation:
Enjoy managing your Docker Compose configurations with ease using this Makefile!