Docker Compose is a powerful tool for defining and running multi-container Docker applications. It allows developers to define the services, networks, and volumes required by an application in a single file, usually named docker-compose.yml. With Compose, you can manage complex applications that consist of multiple interconnected services, such as databases, backend APIs, web frontends, and more, all with simple commands.
In this article, we’ll dive into what Docker Compose is, how it works, and how to use it to streamline your containerized application development.
What is Docker Compose?
Docker Compose simplifies the process of managing multiple Docker containers by providing a way to define the entire stack in a single configuration file. It allows you to:
- Define multi-container applications: Instead of managing each container separately, Docker Compose enables you to define all of them in a single YAML file.
- Automate the setup and teardown: With a few simple commands, you can create and start all the containers for your application, manage their dependencies, and tear them down once you’re done.
- Version control configuration: The docker-compose.yml file is versioned alongside your application’s source code, allowing teams to maintain and share a consistent development and deployment environment.
Key Concepts of Docker Compose
Before diving into the commands, let’s briefly recap the key components of Docker Compose:
- Services: These are the individual containers that make up your application. For instance, a web app might have a service for the frontend and one for the backend.
- Networks: These define how services can communicate with each other. Services on the same network can interact by using their container names as hostnames.
- Volumes: Volumes are used to persist data between container restarts. For example, a database container might use a volume to store its data.
All of these are typically defined in a docker-compose.yml file, which provides a structured way to configure your multi-container applications.
Docker compose file
A Docker Compose file (typically named docker-compose.yml) is a configuration file used by Docker Compose, a tool that allows you to define and manage multi-container Docker applications. The file describes the services, networks, and volumes that your application requires, and it is written in YAML syntax.
With a Docker Compose file, you can define the full stack of your application, including databases, web servers, cache systems, or any other services that your app depends on, and Docker Compose will manage the orchestration of these services for you.
Structure of a Docker Compose File
The Docker Compose file is divided into several sections, but the most common ones are:
- Services: Defines the containers (services) that make up your application. Each service typically corresponds to a Docker container. Each service can have its own configuration, such as the Docker image to use, build context, environment variables, ports to expose, volumes to mount, etc.
- Networks: Specifies how the containers (services) communicate with each other. Docker Compose creates a default network for all services to communicate, but you can define custom networks if needed.
- Volumes: Defines persistent data storage, which is necessary when you want to store data outside the container’s file system. Volumes are useful for data persistence between container restarts or for sharing data between containers.
Example :
services:
web:
image: node:14
container_name: my_web_app
hostname: webapp.local
working_dir: /app
volumes:
- ./web:/app
ports:
- "8080:8080"
environment:
- DATABASE_HOST=db
- DATABASE_PORT=5432
- DATABASE_NAME=mydb
- DATABASE_USER=user
- DATABASE_PASSWORD=password
depends_on:
- db
networks:
- my_custom_network
db:
image: postgres:13
container_name: my_db
hostname: db.local
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- db_data:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- my_custom_network
volumes:
db_data:
driver: local
networks:
my_custom_network:
driver: bridge
Breakdown of the docker-compose.yml
services
- Definition: A service is a container that runs a specific part of your application. Each service is defined with its own configuration (e.g., image, environment variables, ports).
- Example: The web and db are services in the docker-compose.yml file.
image
- Definition: The image field specifies the Docker image to be used for the container. Images can either be pulled from a registry (like Docker Hub) or be built from a Dockerfile.
- Example:
- web: Uses the official node:14 image to run the Node.js web app.
- db: Uses the official postgres:13 image for the PostgreSQL database.
volume
- Definition: A volume is used to persist data outside the container. Volumes are particularly useful for data that should be retained even if the container is stopped or removed (e.g., database data).
- Example:
- The db service uses a volume named db_data to persist PostgreSQL data in /var/lib/postgresql/data.
- The web service mounts a local directory ./web (from the host) to /app inside the container, so any changes made locally reflect inside the container (useful for development).
hostname
- Definition: The hostname field defines the name that the container will use to identify itself on the network. This is particularly useful when containers need to communicate with each other using specific hostnames.
- Example:
- web: The container’s hostname is set to webapp.local.
- db: The container’s hostname is set to db.local.
ports
- Definition: The ports field maps ports from the container to the host machine. This allows external access to the services running inside the container, such as web servers or databases.
- Example:
- web: Maps port 8080 on the container to port 8080 on the host, making the web app accessible via http://localhost:8080.
- db: Maps port 5432 on the container (the default PostgreSQL port) to port 5432 on the host, allowing external access to the database.
depends_on
- Definition: The depends_on field specifies dependencies between services, ensuring that one service is started before another. However, it does not wait for the dependent service to be fully ready — it only waits for the container to be started.
- Example:
- The web service depends on the db service, meaning Docker Compose will start the db container first.
networks
- Definition: The networks field defines the network(s) that services will connect to. By creating a custom network, containers can communicate with each other by their service names (which are used as hostnames).
- Example:
- Both the web and db services are connected to the custom network my_custom_network, which allows the web service to access the db service by the hostname db.local
Running the Docker Compose File
To run this example:
- Create a directory, e.g., my_project.
- Inside that directory, create a subdirectory web that contains your Node.js application code.
- Create the docker-compose.yml file in the root of my_project.
- Run the following command to start the services:
docker-compose up
This will start the services, create the custom network, and mount the volumes. You can then access your web app at http://localhost:8080.
Docker Compose Commands
1. docker-compose up: The docker-compose up command is used to create and start all the services defined in your docker-compose.yml file. If the containers do not exist, Docker Compose will pull the necessary images and create them.
# This command will start the services in the foreground.
docker compose up
# If you want to run it in the background (detached mode), you can add the -d flag.
docker compose up -d
In detached mode, the containers run in the background, and you get the command prompt back.
2. docker-compose down: The docker-compose down command stops and removes all containers, networks, and volumes defined in the docker-compose.yml file. This is useful when you want to clean up your environment after you’re done working with it.
docker compose down
# To stop + remove volumes associated with the services, use the --volumes or -v flag:
docker compose down -v
3. docker-compose build: The docker-compose build command builds or rebuilds services defined in the docker-compose.yml file. This is necessary if you have made changes to the Dockerfiles or service configurations.
docker compose build
# If you only want to rebuild a specific service, you can specify the service name:
docker compose build <service-name>
# The --no-cache option ensures that the images are built from scratch, without using the cache from previous builds. This is useful if you want to make sure all the changes are applied without any stale cache.
docker-compose build --no-cache
4. docker-compose start: The docker-compose start command is used to start existing containers that were previously created with docker-compose up. This command does not create new containers, so it only starts the containers that are stopped but still exist.
docker compose start
# To start a specific service:
docker compose start <service>
5. docker-compose stop : The docker-compose stop command stops running containers without removing them. You can later restart them with the docker-compose start command.
docker compose stop
# To stop a specific service:
docker compose stop <service>
6. docker-compose restart: The docker-compose restart command restarts all running containers by stopping and then starting them again.
docker compose restart
# To restart a specific service:
docker compose restart <service>
7. docker-compose logs: The docker-compose logs command displays the logs for your services. This is useful for debugging and monitoring the output from your containers.
docker compose logs
# You can also follow the logs in real-time using the -f flag:
docker compose logs -f
# To view the logs for a specific service, you can specify the service name:
docker compose logs <service>
8. docker-compose exec : The docker-compose exec command allows you to execute a command inside a running container. This is useful for troubleshooting or performing administrative tasks inside the container.
docker compose exec <service> <command>
# examples
docker compose exec frontend bash
docker compose exec frontend ls /app
9.docker-compose run:
As per the documentation :
- Commands you use with run start in new containers with configuration defined by that of the service, including volumes, links, and other details.
- First, the command passed by run overrides the command defined in the service configuration.
docker compose run <service> <command>
- docker compose run command does not create any of the ports specified in the service configuration. This prevents port collisions with already-open ports. If you do want the service’s ports to be created and mapped to the host, specify the –service-ports
docker compose run --service-ports web python manage.py
# Alternatively, manual port mapping can be specified with the --publish or -p options, just as when using docker run:
docker compose run --publish 8080:80 -p 2022:22 -p 127.0.0.1:2021:21 web python manage.py
10. docker-compose ps : The docker-compose ps command shows the status of the containers in your project. This is useful for seeing which containers are running and checking their status.
docker compose ps
11. docker-compose pull: The docker-compose pull command is used to pull the latest versions of the images for the services defined in the docker-compose.yml file. This is useful when you want to make sure you have the latest images from a remote repository, such as Docker Hub.
docker compose pull
# To pull a specific service’s image:
docker compose pull <service>
12 . docker-compose scale : The docker-compose scale command allows you to scale services by running multiple instances of a service. This is useful for horizontal scaling, such as running multiple instances of a web frontend to handle more traffic.
docker compose up --scale frontend=3
This will start three instances of the frontend service.
13. docker-compose config : The docker-compose config command is used to validate and view the configuration defined in the docker-compose.yml file. It checks for syntax errors and outputs the expanded configuration.
docker-compose config
Conclusion
Docker Compose is a game-changer when it comes to managing multi-container applications. It simplifies the process of defining and running interconnected services, making development, testing, and deployment of containerized applications far easier. Whether you’re building a small web app or a large-scale microservices architecture, Docker Compose is an invaluable tool to have in your development toolkit. By automating container orchestration and providing an easy-to-use configuration file, Docker Compose allows you to focus on building your application rather than managing containers.