Exception Perceptions: 4 Best Practices for Using Docker Compose
On this episode of Exception Perceptions, Bret Fisher, Docker Captain and creator of the popular Docker Mastery series on Udemy, helps us master Docker Compose. Watch the episode, and read more of Bret’s suggested best practices. Then go and get all of his Docker resources.
One of the many strengths of the Docker toolset is that the Maintainers created the tools with developer and operator workflows in mind. The Docker CLI helps with local development and testing for sure, but the real workflow savings are in the docker-compose automation.
Docker CLI vs. docker-compose CLI
The Docker CLI has all the raw power to do everything a system would need to do with the engine daemon. That daemon is always running in the background, and your CLI talks to it over a socket (or SSH or TLS).
The docker-compose CLI is an alternate workflow-focused tool that speaks to the same Docker engine API as the Docker CLI, but it does this scoped to your current "project” (the directory you’re in) and greatly simplifies the commands you type.
Bottom line, if a developer’s first day on your team has to do much more than the following three steps, there's room for improvement in the developer environment set-up with docker-compose:
Install Docker Desktop
Clone repos and install your favorite editor
docker-compose up
That should be (roughly) it!
There's so much you can configure and automate in Docker Compose, but I'd like to give you four best practices I've adopted for making it work the best for typical development workflows.
4 Tips for Making Compose Easier To Use for Development
Focus your Compose file organization and naming on developer happiness.
Always use the default docker-compose.yml file for one-line "start the whole solution" approach, like docker-compose up
. If a new developer has to do more than docker-compose up
, then there's room for improvement in the Compose file. Leave the one-off solutions that require more typing for CI/CD automation, like docker-compose -f filename.yml -f override-name.yml -p project-name up
.
Use docker-compose.override.yml as a way for dev's to set their own custom settings.
Typically, I'll set a git repo's .gitignore
to skip anything *.override.yml
, then make a sample file (I call it docker-compose.override.sample.yml
) and provide lots of comments for how to use it. Then a new developer can simply copy the sample override to the default name that's ignored by git, and customize their environment how they like (usually ports and environment variables).
Use docker-compose.yml
"entrypoint:" YAML object to run a custom entrypoint script, just for dev needs.
Often in dev, you need to do things slightly different on container start-up, like import sample data to the database, create sample users, or run something on your bind-mounted host code like Bower or Gulp. I often have two entrypoint scripts for apps: One is always run, which I call docker-entrypoint
, and then another just for local dev called docker-entrypoint-dev
. Here's an example shell script for PHP where we often have to worry about composer build tools and permissions of cache directories:
#!/bin/sh
set -e
# run last minute build tools just for local dev
# this file should just be used to override on local dev in a compose file
# run default entrypoint first
/usr/local/bin/docker-php-entrypoint
# ensure bind mount permissions are what we need
chown -R :www-data /var/www/app/cache/ \
/var/www/app/bootstrap/cache/ \
/var/www/app/public/ \
/var/www/app/build/
chmod -R g+w /var/www/app/cache/ \
/var/www/app/bootstrap/cache/ \
/var/www/app/public/ \
/var/www/app/build/
# run last minute build tools just for local dev
cd /var/www/app
composer dump-autoload
cd /var/www/app/public
exec "$@"
Use docker-compose.yml command: YAML object to run a different Dockerfile CMD.
A great example of this is to run nodemon for a Node.js app rather than the standard node server. Nodemon, in this case, restarts the node server when files on your localhost change, that are mounted into the container at runtime. Node doesn't need to care about these restarts on normal servers running the image, so it's best to have your Dockefiles set to "CMD node index.js" or similar and then in the docker-compose.yml service for that container, overwrite that command with something like command: ../node_modules/.bin/nodemon ./bin/www --inspect=0.0.0.0:9229
which enables debug as well as runs your app with nodemon.
Maybe the best thing about Docker Compose is how much time it can save you compared to previous solutions for creating and managing development environments for different projects. It works just as well in teams.
Most of what I've learned in Docker Compose has come from trying things I've learned in the Docker Docs, as well as the teams I work with to make development, testing, and deployments easier. I share these learnings everywhere I can, and I encourage you to do the same.
What other features or team standards have you found useful with Compose? Please share with me and the community on Twitter.