Intro to Docker Compose
I enjoy using Docker for individual applications, but typing out each
application’s commands (i.e.
docker run <option_1> <option_2> image
command) becomes tedious and error prone, especially across
Also, each application has its own set of commands, that are in general similar, but have small idiosyncracies. For example: the commands to start a Ruby on Rails application and Angular application in development mode:
ng serve --host 0.0.0.0
rails server -b 0.0.0.0
Do you see the difference? Ruby on Rails uses server and
its host address while Angular uses serve and
--host for similar
settings. Such idiosyncracies become an annoyance over time.
At the same time, I am only working with two services, but with five or six applications, managing them would take exponentially more work.
Solution: Enter Docker Compose
The Docker Compose utility solves these problems if your individual applications can run in a Docker container.
I will take two docker containers I have worked on
number_generator_app and Angular
and write a docker compose configuration file to bring both services
up ready for development with a single command.
The main requirement for this article is:
- Install Docker
If you would like to follow along, everything for this article is also available here
What is Docker Compose?
From Docker’s documentation website, Docker Compose is:
Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.
In my own words:
Docker Compose is a Docker container orchestration utility. Alowing users to seamlessly manage multiple containers.
To gain the full benefits of Docker Compose:
- there is more than one application - like a microservice
- each application can run in a Docker container
If either one of these are not true in your situation, Docker Compose will seem like extra work.
Docker Compose is an application, but it requires a user generated configuration file. The main use for the configuration file is:
- specify each application location
- starting command for the application
- port assignments
A configuration file to setup the Ruby on Rails number_generator_app server with the number-generator-service Angular application would be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 version: '3.2' services: rails: build: number_generator_app/ command: bundle exec rails s -p 3000 -b '0.0.0.0' ports: - "3100:3000" volumes: - type: bind source: ./number_generator_app/ target: /rails_app angular: build: number-generator-service/ command: ng serve --host 0.0.0.0 ports: - "4300:4200" volumes: - type: bind source: ./number-generator-service/ target: /angular
I will go over each key section:
The services entry lists all the top level application. In our case,
angular are the top level applcations. These are just
labels and can easily modified to:
The build option specifies the directory where to find the application, relative to the docker-compose.yml file.
Docker compose expects the application folder to have a Dockerfile that specifies how to build the Docker image to run the application.
The command specifies how to start the application within the Docker container.
This command can be in the Dockerfile directly as
CMD <command with
options> but I prefer to have the command in the docker-compose
Having the command in the configuration file allows the Docker image to be more flexible (i.e. using another pre-built Docker image directly.)
Specify the exposed ports in this option. As we have a frontend application connecting to a backend application through the host, these ports must match the ports specified within the application.
This section details the volume sharing option between the host and the container. If you would like to load the application code from the host machine dynamically (i.e. local host changes reflect directly within the running container) include options to mount volumes into the container.
If the Docker image contains all of your application code, this configuration is optional.
- only appears on the
type option, otherwise errors such
as: “‘type’ is a required option”_starts to appear when running
For example, if we write the voluems section as:
Then this error will appear:
With the docker-compose configuration file complete and applications are in place, we have to build any associated Docker images.
The easiest way to do that is use command:
This will build all the docker images in the configuration file.
If there are any changes to the Dockerfile for applications listed in
the Docker compose configuration file, use
again to rebuild everything. Docker compose is smart enough to only
build images with changes.
With all Docker images listed in the docker compose configuration file built, it’s time to bring up the application. To start up every application, run:
Yes, it is this simple. This is better than manually starting each one, which can become tedious and error-prone.
To stop the application(s), you can type:
^C if you still have the
console open or run:
After working with this new setup, I started to get this error:
This is peculiar as I did not run the Rails server. Shutting down the
docker-compose stop showed the server was not
running… So I was curious.
The solution I found was to remove the
/rails_app/tmp/pids/server.pid file (listed in the error message!)
and I was able to start the server again normally.
Instead of starting and stopping individual applications’ by hand, Docker Compose provides a simple one command method for this.
Its configuration file centralizes all of applications’ individual settings so it can be easily managed across restarts or even systems.