Practical Exercises for Docker Compose: Part 1

By Alwyn Botha, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud’s incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

This set of tutorials focuses on giving you practical experience on using Docker Compose when working with containers on Alibaba Cloud Elastic Compute Service (ECS). This tutorial focuses on you using the shell a lot: interactively exciting.

After you completed this set of 5 tutorials you can spend time reading the official Docker Compose documentation: Everything will make a lot more sense since you now have actual practical (simple) experience with most of the Docker Compose instructions.

Part 1 of 5 demonstrates several docker-compose configuration options that can be explored in isolation. Every separate heading section below explores just ONE docker-compose option — making the config files very short and very easy to understand.

Let’s get started.


You need access to a server/VPS with a recent version of Docker already installed. In this tutorial, I will be using an Alibaba Cloud Elastic Compute Service (ECS) instance with CentOS and Docker.

You don’t need to purchase a large instance to follow this tutorial. Even when swarms of 6 containers are running in later tutorials you still need a server with only a total RAM of 512MB.

Although I am writing this tutorial using CentOS, you can also use Debian or Ubuntu. A large majority of this tutorial will work on any Linux distributions since it mostly uses Docker Compose commands.

You need a very basic understanding of Docker, images, containers and using docker-compose and docker ps -a. You need to have used docker-compose up around 3 times to roughly understand its concepts.

The purpose of this tutorial is to get you to docker-compose up several very simple docker compose files. Your understanding of Docker compose concepts will grow as you use it.

Clean Up Preparation

It will really help if you have only a few containers (preferably none) running. That way you can easily find your tutorial container in docker ps -a output lists.

So stop containers and removes containers created by docker-compose up:

(Skip this step if you don’t have any)

So stop and prune all the containers you do not need running.

You can quickly do that (in your DEVELOPMENT environment) using:

To now remove all containers, run

Separate Directory and Editor

Its better to have your docker-compose files in separate directories. One directory per docker-compose service / stack or swarm: neat, organized, small, efficient, understandable in its entirely due to all these attributes.

Therefore, enter:

There are two editors frequently used in Linux distros: vim and nano. I prefer nano, but vim should work fine as well.

To get nano installed:

Debian / Ubuntu :

CentOS :

Nano beginners guide:

Those are the only things you need to know to use nano for all 5 of these docker-compose tutorials.


This works identical to image in Dockerfiles. Image specifies the image to build the container from.

Every exercise below uses it.


Reference :

tmpfs mounts a (in RAM) temporary file system inside the container. It can be a single value or a list. Let’s do a demo with a list of 2 temp file systems:

Add the following to your docker-compose.yml using

version: "3.7"
image: alpine:3.8 command: sleep 600 tmpfs: - /my-run:size=10M - /my-app-tmp:size=20M

docker-compose up brings up the services defined within your docker-compose.yml file.
docker exec enters a running container — very similar to ssh into servers.

Run :

You should see the size of our 2 temp file systems:

Perfect. Mounted as requested, sizes as requested. Zero percent used.

Let’s write 15M of zeros to /my-run (10M in size)

The dd command does not show an error, but $? gives the exit status of the last command: in this case a 1 : meaning error.

Run df again:

/my-run fully used as expected. docker-compose successfully created our tmpfs the correct size and it works as expected.

Let’s write 15M of zeros to /my-app-tmp (20M in size)

echo $? shows zero = success. Lets check /my-app-tmp used size:

15M used. Perfect.

You now know how to define tmpfs to the correct sizes needed for the applications running in your containers.

You can use man dd to learn more about the dd command.

Spoiler: if = input file, of = output file, bs = block size. Simple as that.

env_file and environment

env_file and environment adds environment variables to your containers.

Environment variables declared in the environment section override env_file values.

Nearly all Linux applications have environment variables, so you need to know how to inject it into your containers. (The only application I know that does not need this is a hello word application)

To see how this works, we need some prep work:

  1. create environment files
  2. refer to those files via docker-compose
  3. then enter / exec container to see if environment variables is defined as expected.

Add the following environment variables to these files in your current directory :



For the 3rd file, to save you some typing, just cut and paste the text below to your shell prompt:

(The cat sends all the ENV lines to mybestvars.env)

You can learn more about it here:

Can you already see how similarly named variables will be overridden?

Add the following to your docker-compose.yml using

version: "3.7"
image: alpine:3.8 command: sleep 600 env_file:
- myvars.env
- mynewvars.env
- mybestvars.env
- ENV_VAR_A=42
- ENV_VAR_E=Apache web server


Execute printenv as shown:

How were those values determined?

ENV_VAR_A=42 … the value from environment: in the docker-compose file.
environment: value overrides all the values set in all 3 those files.

That is how environment variables work. Later files override similar named variables in previous files. BUT environment: in the docker-compose file overrides ALL.

Those 3 files do not exist in the container, but their content lives on in those environment variables.

Important: the 3 environment files you created in your HOST shell is important. They are part of your docker-compose file and should be kept in source control, along with your docker-compose file.


extra_hosts links hostnames to their ip addresses in the /etc/hosts file inside the container.

Add the following to your docker-compose.yml using

version: "3.7"
image: alpine:3.8 command: sleep 600 extra_hosts:
- "myhost:"
- "myotherhost:"


docker exec -it compose-tuts_alpine_1 /bin/sh

Enter commands shown: ip r : shows routing table and cat /etc/hosts shows hosts file contents.

Docker adds nothing to routing tables to enable the container to find those 2 new hosts. ALL it does is add those 2 mappings to the /etc/hosts file.

Those hostname mappings can only contain fixed ip addresses.

That last random looking hostname is the hostname of this container. If you do a hostname command at the CONTAINER prompt you will see that as the hostname.


If you are already familiar with Dockerfile health checks, docker-compose health checks are easy: its just some minor syntax differences.

Add the following to your docker-compose.yml using

version: "3.7"
image: alpine:3.8 command: sleep 600 healthcheck:
test: exit 0
interval: 1s
timeout: 1s
retries: 3
start_period: 0s


docker ps -a

If you have a speedy computer you will see this: health checks … still starting.

On a slower computer / server you will have to run the docker ps -a command multiple times before you can complete the health checks successfully, showing: (healthy)


Edit your docker-compose.yml — modify around line 10 to look like:

Now the health check will fail completely — with error exit code 1.


Within seconds you will see: (unhealthy)

Depending on your application, you have to determine which exact health checks are appropriate for each specific application.

You also have to determine an appropriate interval: and timeout value.

One second for both of those is too low for production. 1 second is used here for quick testing and demo purposes only.

Similarly start_period cannot be zero in real life. You should give your application time to start up first, BEFORE you start running health checks.

You can leaern more about health checks on Alibaba Cloud on their official documentation: Container Service Best Practices — Health Check of Docker Containers

The Docker community provides some instance images that contain health checks at

Even if you do not use the software in those examples you can still get very good ideas of how to write your own health checks.


Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store