How to use Docker Compose for multi-container applications
Compose simplifies scaling and deployment of applications in Docker by automating container management. Our tutorial takes an in-depth look at setting up and using Docker Compose to streamline your application deployment process.
What is Docker Compose?
Docker Compose is used to manage applications and increase efficiency in container development. Configurations are defined in a single YAML file, making applications easy to build and scale. Docker Compose is often used to set up a local environment. However, it can also be part of a Continuous Integration / Continuous Delivery (CI/CD) workflow. Developers can define a specific container version for testing or specific pipeline phases. This makes it easier to identify issues and fix bugs before the application moves into production.
Docker Compose requirements
For container orchestration, 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 standalone binaries.
- Docker Desktop: Development environment with graphical user interface including Docker Engine and Docker Compose.
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 demonstrate how to use Docker Compose with a simple Python web application that utilises 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
shellChange to the directory.
$ cd composedemo
shellCreate 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)
pythonIn our setup, we utilise redis
as the hostname and the default port 6379
for connecting to the Redis service. Additionally, we specify that the get_hit_count()
function should make multiple connection attempts to the service. This is recommended where Redis may not be immediately available when the application starts or there may be intermittent connection issues during runtime.
Create the file requirements.txt with the dependencies:
flask
redis
plaintextStep 2: Set up Dockerfile
The Dockerfile is used for the Docker image. This specifies all the dependencies that the Python application requires.
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"]
shellWe instruct Docker to utilise the Python 3.7 image. Furthermore, we set the necessary environment variables for the flask command. By using apk add
, we install essential dependencies, 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 Dockerfile was saved without a file extension, as some editors automatically 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"
yamlThe web service is built using the Docker image created by the Dockerfile. It associates 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 application with Compose
Launch the application from your project folder.
docker compose up
shellCall up http://localhost:8000 in your browser. You can also enter http://127.0.0.1:8000.
You should see the following message:
Refresh the page. The number of views should now have increased by 1.
Stop the application using:
$ docker compose down
shellTo stop running the application, 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"
yamlUnder the Volumes section, we specify the attachment 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 development mode.
Step 6: Rebuild and run application
Enter the following command in the terminal to rebuild the Compose file:
docker compose up
shellStep 7: Update the application
Now that you’re using a bind mount for your application, you can modify your code and automatically see changes without rebuilding the image.
Write a new welcome test in app.py.
return 'Hello from Docker! I was here {} times.\n'.format(count)
pythonRefresh the browser to test whether the changes have been applied.
Step 8: other commands
The --help
option lists available Docker Compose commands:
docker compose --help
shellTo run Docker Compose in the background, you can add the -d
argument:
docker compose up -d
shellUse down
to remove all containers. The --volumes
option deletes the volumes used by the Redis container.
docker compose down --volumes
shellTo get started with Docker, check out our Docker Tutorial and our overview of Docker Commands.