Compose sim­pli­fies scaling and de­ploy­ment of ap­pli­ca­tions in Docker by au­tomat­ing container man­age­ment. Our tutorial takes an in-depth look at setting up and using Docker Compose to stream­line your ap­pli­ca­tion de­ploy­ment process.

What is Docker Compose?

Docker Compose is used to manage ap­pli­ca­tions and increase ef­fi­cien­cy in container de­vel­op­ment. Con­fig­u­ra­tions are defined in a single YAML file, making ap­pli­ca­tions easy to build and scale. Docker Compose is often used to set up a local en­vi­ron­ment. However, it can also be part of a Con­tin­u­ous In­te­gra­tion / Con­tin­u­ous Delivery (CI/CD) workflow. De­vel­op­ers can define a specific container version for testing or specific pipeline phases. This makes it easier to identify issues and fix bugs before the ap­pli­ca­tion moves into pro­duc­tion.

Docker Compose re­quire­ments

For container or­ches­tra­tion, you need both Docker Engine and Docker Compose. Ensure you’ve got one of the following installed on your system:

  • Docker Engine and Docker Compose: Can be installed as stand­alone binaries.
  • Docker Desktop: De­vel­op­ment en­vi­ron­ment with graphical user interface including Docker Engine and Docker Compose.
Tip

Find out how to install Docker Compose on different operating systems in our tutorials:

Step-by-step guide of how to use Docker Compose

In the following, we demon­strate how to use Docker Compose with a simple Python web ap­pli­ca­tion that utilizes a hit counter. To do this, we use the Python Flask framework and the Redis in-memory database. You don’t need to install Python or Redis, as they are provided as Docker images.

Step 1: Create project files

Launch the terminal and create a new folder for the project.

$ mkdir composedemo
shell

Change to the directory.

$ cd composedemo
shell

Create the file app.py in this folder and add the following code to it:

import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)
@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I was here {} times.\n'.format(count)
python

In our setup, we utilize redis as the hostname and the default port 6379 for con­nect­ing to the Redis service. Ad­di­tion­al­ly, we specify that the get_hit_count() function should make multiple con­nec­tion attempts to the service. This is rec­om­mend­ed where Redis may not be im­me­di­ate­ly available when the ap­pli­ca­tion starts or there may be in­ter­mit­tent con­nec­tion issues during runtime.

Create the file re­quire­ments.txt with the de­pen­den­cies:

flask
redis
plaintext

Step 2: Set up Dock­er­file

The Dock­er­file is used for the Docker image. This specifies all the de­pen­den­cies that the Python ap­pli­ca­tion requires.

# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
shell

We instruct Docker to utilize the Python 3.7 image. Fur­ther­more, we set the necessary en­vi­ron­ment variables for the flask command. By using apk add, we install essential de­pen­den­cies, including gcc. To allow the container to monitor port 5000, we specify EXPOSE. Using COPY, we transfer the contents of the current folder to the working directory /code within the container. Finally, as default command for the container we choose flask run.

Check that the Dock­er­file was saved without a file extension, as some editors au­to­mat­i­cal­ly append the .txt suffix.

Step 3: Create YAML file

In docker-compose.yml we configure the services “redis” and “web”.

version: "3.9"
services:
    web:
        build: .
        ports:
            - "8000:5000"
    redis:
        image: "redis:alpine"
yaml

The web service is built using the Docker image created by the Dock­er­file. It as­so­ciates the container and the host computer with port 8000, while the Flask web server runs on port 5000. The Redis image, on the other hand, is obtained directly from the official Docker Hub.

Step 4: Run the ap­pli­ca­tion with Compose

Launch the ap­pli­ca­tion from your project folder.

docker compose up
shell

Call up http://localhost:8000 in your browser. You can also enter http://127.0.0.1:8000.

You should see the following message:

Image: Docker Compose Application: Output the number of visits in the browser
You’ll see the number of times you have visited the browser.

Refresh the page. The number of views should now have increased by 1.

Image: Calling the Docker Compose application again
The number of visits increased by 1.

Stop the ap­pli­ca­tion using:

$ docker compose down
shell

To stop running the ap­pli­ca­tion, you can simply press Ctrl + C in the terminal.

Step 5: Add a bind mount

If you want to add a bind mount for the web service, you can do this in docker-compose.yml.

version: "3.9"
services:
    web:
        build: .
        ports:
            - "8000:5000"
        volumes:
            - .:/code
        environment:
            FLASK_DEBUG: "true"
    redis:
        image: "redis:alpine"
yaml

Under the Volumes section, we specify the at­tach­ment of the current project folder to the /code directory inside the container. This allows for seamless code changes without the need to recreate the image. The variable FLASK_DEBUG tells flask run to run in de­vel­op­ment mode.

Step 6: Rebuild and run ap­pli­ca­tion

Enter the following command in the terminal to rebuild the Compose file:

docker compose up
shell

Step 7: Update the ap­pli­ca­tion

Now that you’re using a bind mount for your ap­pli­ca­tion, you can modify your code and au­to­mat­i­cal­ly see changes without re­build­ing the image.

Write a new welcome test in app.py.

return 'Hello from Docker! I was here {} times.\n'.format(count)
python

Refresh the browser to test whether the changes have been applied.

Image: Docker Compose application: modified welcome text
The welcome text in the Python ap­pli­ca­tion has been modified

Step 8: other commands

The --help option lists available Docker Compose commands:

docker compose --help
shell

To run Docker Compose in the back­ground, you can add the -d argument:

docker compose up -d
shell

Use down to remove all con­tain­ers. The --volumes option deletes the volumes used by the Redis container.

docker compose down --volumes
shell
Tip

To get started with Docker, check out our Docker Tutorial and our overview of Docker Commands.

Go to Main Menu