In this post we want to look at ways to orchestrate containers with docker compose instead of doing this manually all the time.

We will look at the following points:

  • Why should you utilize docker-compose
  • How does it work on a conceptual level
    • yaml file
    • example
    • command reference

Why docker compose?

Often when we are working with docker we want to have more than one containerized application working together in some form.
Then in most cases they depend on a SDN (software defined network), other containers to be available and different configurations like volumes and such.

One way to orchestrate this is to start every container by hand and then link them together in a network or with the deprecated –link flag.
Another way could also be to write a script that automates this for you.
But as you can probably tell this will be error prone and not the generic solution you would like to have for this kind of problem.

This is were docker-compose comes into play.
With docker-compose you get a standard to describe complex applications in a consistent and predictable manner which is done by a docker compose file.
Behind the scenes a docker-compose sets up all the needed infrastructure like SDNs and volumes for all specified containers.

The docker-compose.yml file contains all the images to be build and container run instructions you would normally do manually from the command line.

In essence docker-compose constructs the application containers and the relationship between them.

You can also use docker-stack for such orchestration but we will look at this with another post on docker swarms because docker stack works for swarms on a cluster as docker-compose works locally on a single machine. (Although for clusters you probably want something like kubernetes)
Although to replicate docker-compose you could create a swarm with a single machine, but we will look into docker swarm and docker stack in another introduction. Docker-compose is also easier to grasp at first.

How does docker compose work?

To work with docker-compose you need to set up a docker-compose.yml file in your working directory.
This is a yaml file that describes all the build and container run instructions you would normally employ by the docker command line tools.

If you are not familiar with yaml following are the most important rules in short:

  • new Section
  • # comments one line
  • strings are either in “double” or ‘single’ quotes
  • Lists are defined with hyphens like so
    • – list_item_1
    • – list_item_2
    • – list_item_3
  • it is also allowed to define a list like so:
    • [list_item1, list_item_2, list_item3]
  • dictionaries are either defined with a colon or with curly brackets
    • key: value
    • { key: value, key2:value2 }
  • | defines a block where carriage return is preserved
  • > defines a line with no carriage return but with empty lines preserved
  • You are not allowed to use tabs but only spaces
  • To analyze yaml files you can use python shyaml to read yaml files and check them for validity.

Pitfalls of docker-compose:

Be aware that docker-compose does not wait for a container to be ready (whatever this means in terms of your application) to start a dependent container.
A common scenario here is when a database container is setup while another (web) application is trying to access this database. I have a docker-compose recipe where this can be the case and have some external soultions presented in the advanced scenarios there.

Nevertheless the best way to handle this will be from inside the application. This is because for distributed systems you should always be prepared for failures in the system anyways.

Hands on docker-compose

For a simple hands-on see my recipe on how to use docker-compose with a local wordpress installation depending on a maria db backend database.

Be aware that I am using macOS which can lead to some issues on Linux or Windows versions (e.g. with the volume because there is some issues with the docker daemon running in a virtual machine on macOS)

Next up we will look at the most important commands inside a docker-compose.yml file to be used.


Do you pull an image from a repository like the docker-hub or do you utilize dockerfile, both is possible with the following syntax.

   build: <directory with Dockerfile>
   # or
     context: .
     dockerfile: Dockerfile


  • Always creates an own network for the composed containers
  • If you want to use your own SDN you need to create them in the file:
           - my_net_1
    # Top Level
        name: host


Make the opened ports in the container available to the outside world

  - "8082:80"
  - "8443:443"

always:<maps host_port:container_Port

if you only want to expose the port(s) only in the container network (SDN) then use expose

  - "7500"
  - "7501"


The key word volumes follows a list of folders where you want the volumes of the image to be put.
Again the mapping looks like: volume_mapping_host:volume_mapping_container

  - /tmp/db:/var/lib/mysql

# path can be relative to docker-compose.yml file
# ./<path>
#  be aware that you cannot map a file to a directory and vice versa:
# not allowed!!
  - ./default.conf:/etc/nginx/conf.d/
# use like so
  - ./default.conf:/etc/nginx/conf.d/default.conf

If you want to use the volume for more than one service you need to specify it as a top level section

version: '3'

      - webdata:/var/www/html

      - webdata:/var/www/html


Environment variables

The environment key word follows a list or key value pairs to specify your envrionment variables for a service.
The following listings are equivalent

  WORDPRESS_DB_NAME: docker_compose_db

  - WORDPRESS_DB_NAME=docker_compose_db

If you have a lot of environment variables to be set you can also utilize a environment config file like so:

env_file: <path_to_file>


You can also override the Entrypoint of the container or the command that is executed in a container on the start.

entry point: [""]
command: ["/bin/bash"]

command: ["php", "-a"]
# or
  - "php"
  - "-a"


You can read secretes like database passwords and such from a file by creating a toplevel secrets directive.

version: '3'

    image: alpine
      - password

    file: ./top-secret.txt

Restart behavior

With this you can control how the container restarts if the running docker host is stopped and restarted, or if the container fails during its lifetime. The default for this is no.
The other options are:

  • always
  • on-failure
  • unless-stopped

Docker-compose command reference

Last but not least here is some reference of the docker-compose commands that can be used.

  • docker-compose config checks if syntax of docker-compose.yml is correct
    be aware that this sorts all the keys alphabetically
  • docker-compose down stops all in docker-compose.yml described services, removes networks and volumes
  • docker-compose events shows all events that happened for the container that were setup with compose
  • docker-compose kill stops all containers without hesitation (do only use when docker-compose down does not work)
  • docker-compose logs <servicename> shows container logs (only works for running containers), better use docker-compose up without -d flag
  • docker-compose pause stops execution, unpause starts it again
  • docker-compose ps lists all containers of a docker-compose.yml
  • docker-compose rm removes all stopped containers, with -v all volumes are removed too
  • docker-compose run <servicename> starts a single container and its dependencies if specified by depends_on
  • docker-compose start/stop/restart says exactly what it does for all containers of yml file
  • docker-compose top shows all the processes of the containers with its UID,PID,STIME,TIME CMD and other information
  • docker-compose up launches all the containers that are listed in the top-level section of the docker-compose.yml. It creates a network for those containers so that those can communicate. All container tags will be prefixed with the current directory. For debugging you can leave the -d and have all loggings from the services displayed on the terminal


Leave a Reply