How to Install Docker Compose on Ubuntu 24.04 [Step-by-Step]
Docker Compose is a command-line tool for defining and running multi-container Docker applications using a single YAML configuration file. Instead of managing each container separately with individual docker run commands, Compose lets you declare all your services, networks, and volumes in one file and launch everything with a single command.
In this tutorial, you will learn how to install Docker Compose on Ubuntu 24.04, configure a multi-container LAMP stack, manage the container lifecycle, and work with essential Compose features like networking, volumes, and environment variables.
#What is Docker?
Docker is an open-source containerization platform that lets you seamlessly build, share, deploy, and orchestrate applications anywhere, be it on Linux, Windows, Mac, or any other computing environment.
Docker packages applications and their dependencies into containers. Each container runs as an isolated process that shares the host operating system's kernel. Containers are lightweight compared to virtual machines because they do not need a separate OS for each instance. For a deeper look at how Docker works under the hood, read our guide on Docker architecture.
#What is Docker Compose?
Built on top of Docker Engine, Docker Compose is a tool for defining and running multi-container applications. Compose reads a YAML file (named compose.yaml or docker-compose.yml) that describes your application's services, networks, and volumes. It then creates and manages all required containers according to that configuration.
#What does Docker Compose do?
Docker Compose creates containers and other resources defined within the Compose file written in YAML format. With the compose file in place, you can create and launch your multi-container application using the docker compose up command, as we shall see later.
A single Compose file can define web servers, databases, caching layers, message brokers, and any other service your application needs. Compose also handles dependencies between services. If your web application depends on a database, Compose starts the database container first and waits for it to become ready before starting the application container.
#How to use Docker Compose?
Docker Compose is used for providing simplified control when managing multi-container applications. It simplifies the complexities involved in running multiple containers with interlinked services. In addition, it also makes it easier to collect logs and debug issues in multiple containers compared to Docker.
The typical workflow involves three steps. First, write a Dockerfile for each service that needs a custom image (or reference existing images from Docker Hub). Second, define all your services in a compose.yaml file. Third, run docker compose up to start everything. For a quick reference of all available commands, see our Docker Compose cheat sheet.
Ready to supercharge your Docker infrastructure? Scale effortlessly and enjoy flexible storage with Cherry Servers bare metal or virtual servers. Eliminate infrastructure headaches with free 24/7 technical support, pay-as-you-go pricing, and global availability.
#Prerequisites
Before you set sail, ensure you have the following in place:
-
An instance of Ubuntu 24.04 with SSH access;
-
A sudo user configured on the server;
-
Docker installed on your Linux instance.
-
Docker Engine 24.0 or later (required for Compose V2 plugin support). Run
docker --versionto confirm.
#How to install Docker Compose on Ubuntu 24.04
The below five steps will show you how to install Docker Compose on Ubuntu 24.04. I've included how to update package lists, install Docker Compose, deploy LAMP stack using Docker Compose, run Docker Compose, and learn how to pause, stop, and remove containers.
#Step 1: Update Package Lists
First, log in to your server and update the package lists as follows.
sudo apt update
This command downloads and updates package information defined in the /etc/apt/sources.list file and /etc/apt/source.list.d directory.
#Step 2: Install Docker Compose
Docker Compose is hosted on Ubuntu repositories and can easily be installed by running the command:
sudo apt install docker-compose -y
However, the version installed from the repositories does not give you the latest version of Docker Compose. To get the most updated version of Docker Compose, download it from its official GitHub repository.
Before downloading the latest Docker Compose plugin binary, create a cli-plugins directory in your home folder.
mkdir -p ~/.docker/cli-plugins/
Next, download the Docker compose plugin file to the ~/.docker/cli-plugins/ directory using the curl command as shown.
curl -SL https://github.com/docker/compose/releases/download/v2.40.3/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
Replace v2.40.3 with the latest version from the releases page if a newer version is available.
Run the ls command to verify the presence of the docker-compose file.
ls -lh ~/.docker/cli-plugins
Outputtotal 74M
-rw-r--r-- 1 root root 74M Feb 26 06:27 docker-compose
Next, assign execute permissions to the file using the chmod command.
chmod +x ~/.docker/cli-plugins/docker-compose
To confirm the installation was successful, check the Docker compose version.
docker compose version
OutputDocker Compose version v2.40.3
#Step 3: Deploy LAMP stack using Docker Compose
With Docker Compose already installed, we will test it by deploying a LAMP stack application. LAMP ( Linux Apache Mysql PHP ) is a popular web hosting stack used to host websites.
To get started, create the project directory and navigate into it. In this case, our project directory is my-demo-app.
mkdir ~/my-demo-app
cd ~/my-demo-app
Next, create a docker-compose.yml file.
nano docker-compose.yml
Add the following lines of code:
services:
apache:
image: php:apache
container_name: apache-app
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
depends_on:
mysql:
condition: service_started
networks:
- lamp-network
mysql:
image: mysql:8.0
container_name: mysql-app
restart: always
environment:
MYSQL_ROOT_PASSWORD: mysql_root_password
MYSQL_DATABASE: mydatabase
MYSQL_USER: myuser
MYSQL_PASSWORD: myuser_password
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
networks:
- lamp-network
networks:
lamp-network:
driver: bridge
volumes:
mysql-data:
The configuration above sets up two services:
-
apache: This is the Apache web service with PHP support. It runs as a container called
apache-appcreated from thephp:apacheimage. The webserver will serve files from the.srcdirectory which will be created in the project folder. The webserver will run on port80on the container which will be mapped on port8080on the host system. -
mysql: This is the MySQL database service with a specified root password, database name, MySQL username, and password. The service runs as a container called
mysql-appon port3306which is mapped on port3306on the host system.
Save the changes in the YAML file and exit.
#Optional: Use an environment file for sensitive values
Storing passwords directly in the Compose file is convenient for testing but risky for production. You can move sensitive values to a .env file in the same directory as your docker-compose.yml:
MYSQL_ROOT_PASSWORD=mysql_root_password
MYSQL_DATABASE=mydatabase
MYSQL_USER=myuser
MYSQL_PASSWORD=myuser_password
Then reference these variables in the Compose file using the ${VARIABLE} syntax:
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
Docker Compose automatically reads the .env file from the project directory. Add .env to your .gitignore to keep secrets out of version control.
#Step 4: Run Docker compose
Having defined the services to be created, the next step is to start the LAMP stack using Docker Compose. To do so, run the following docker compose command.
docker compose up -d
The command downloads the specified Docker images, in this case php:apache and mysql:8.0. Afterward, it creates the mysql-app and apache-app containers and runs them in detached mode using the -d flag.
Additionally, it creates a default network for communication between the services, the mysql-data persistent directory for the database, and the src web directory for storing website files.
Output[+] Running 3/3
✔ Network my-demo-app_lamp-network Created 0.0s
✔ Container mysql-app Started 0.3s
✔ Container apache-app Started 0.5s
To verify currently running containers, run the docker ps command:
docker ps
You will get the following output showing all running containers and their details such as status and network ports.
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8aa5c35c7ad7 php:apache "docker-php-entrypoi..." About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp apache-app
1158f45d75e6 mysql:8.0 "docker-entrypoint.s..." About a minute ago Up About a minute 0.0.0.0:3306->3306/tcp, [::]:3306->3306/tcp, 33060/tcp mysql-app
You can also list the existing images as shown.
docker images
OutputIMAGE ID DISK USAGE CONTENT SIZE EXTRA
mysql:8.0 a3dff78d8762 1.08GB 247MB U
php:apache 9be84c47f279 744MB 189MB U
You can check the logs generated by the running containers using the docker compose logs command.
docker compose logs
Outputmysql-app | 2026-02-26 04:39:55+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.45-1.el9 started.
apache-app | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
apache-app | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.18.0.3. Set the 'ServerName' directive globally to suppress this message
mysql-app | 2026-02-26 04:39:55+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
mysql-app | 2026-02-26 04:39:55+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.45-1.el9 started.
apache-app | [Thu Feb 26 04:39:55.987084 2026] [mpm_prefork:notice] [pid 1:tid 1] AH00163: Apache/2.4.66 (Debian) PHP/8.5.3 configured -- resuming normal operations
…
To follow logs in real time (similar to `tail -f`), add the `-f` flag:
```bash command
docker compose logs -f
To view logs for a specific service only:
docker compose logs apache
At this point, we will create a simple webpage to test the web server. So, navigate to the web directory called src, which is located in the project folder.
cd src
Create an index.html file.
sudo nano index.html
Here's some sample code you can use to define the welcome page.
<!DOCTYPE html>
<html>
<body>
<h1>LAMP stack successfully deployed using Docker compose!</h1>
</body>
</html>
Save the changes and exit the file. Launch your browser and head over to the server's URL as shown:
http://server-ip:8080
You should get the webpage below, proof that the webserver is running as expected.
To connect to the mysql-app database container using the MySQL user specified in the YAML file, run the command:
sudo docker exec -it mysql-app mysql -u myuser -p
Provide the user's password and hit ENTER to access the MySQL shell as shown.
sudo docker exec -it mysql-app mysql -u myuser -p
OutputEnter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.45 MySQL Community Server - GPL
Copyright (c) 2000, 2026, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydatabase |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
mysql>
To exit the shell and the container by extension, type exit and hit ENTER.
#Step 5: Pause, stop, and remove containers
To pause the Docker compose environment execution without altering the state of the containers, use the command:
docker compose pause
Output[+] Pausing 2/2
✔ Container mysql-app Paused 0.0s
✔ Container apache-app Paused 0.0s
To resume execution or unpause the environment, run the command:
docker compose unpause
Output[+] Running 2/2
✔ Container mysql-app Unpaused 0.0s
✔ Container apache-app Unpaused 0.0s
To stop the environment without removing the containers, run:
docker compose stop
Output[+] Stopping 2/2
✔ Container apache-app Stopped 1.2s
✔ Container mysql-app Stopped 1.3s
To stop and remove containers and the default network created by Docker Compose, run the command:
docker compose down
Output[+] Running 3/3
✔ Container apache-app Removed 0.0s
✔ Container mysql-app Removed 0.0s
✔ Network my-demo-app_lamp-network Removed 0.1s
To also remove the named volumes defined in the Compose file (this deletes all persistent data, including database contents):
docker compose down -v
To remove everything, including images pulled by the Compose project:
docker compose down --rmi all -v
Use --rmi all with caution. It removes all images used by the services, so the next docker compose up will re-download them from the registry.
#Essential Docker Compose commands
Beyond the basic up, down, and stop commands, Docker Compose provides several other commands for day-to-day container management. For a full reference, see our Docker Compose cheat sheet.
#Check service status
docker compose ps
Lists all containers managed by the current Compose project along with their state, ports, and health status.
#Restart a specific service
docker compose restart apache
Restarts the apache service without affecting other services. Compose stops the container and starts a new one using the same configuration.
#Scale a service
docker compose up -d --scale apache=3
Runs three instances of the apache service. Scaling requires removing the container_name directive from the service definition, as Docker cannot assign the same name to multiple containers. You also need to remove fixed host port mappings (like "8080:80") or use a range (like "8080-8082:80").
#Execute a command inside a running container
docker compose exec mysql bash
Opens a bash shell inside the running mysql container. Unlike docker compose run, the exec command connects to a running container.
#Pull updated images
docker compose pull
Downloads the latest versions of all images specified in the Compose file. After pulling, run docker compose up -d to recreate containers with the updated images. For more details on the update workflow, see our guide on how to update Docker images.
#Validate the Compose file
docker compose config
Parses and validates the Compose file, then prints the resolved configuration. Use this to catch YAML syntax errors and verify that environment variable substitutions resolve correctly before running docker compose up.
#Docker Compose networking
Docker Compose automatically creates a default network for each project. Every container in the project joins this network and can reach other containers using the service name as a hostname. For example, the apache service connects to MySQL using the hostname mysql (the service name defined in the Compose file).
#Custom networks
You can define custom networks for more control over container communication. The LAMP stack example above uses a custom bridge network called lamp-network. Custom networks let you isolate groups of services from each other within the same Compose project.
networks:
frontend:
driver: bridge
backend:
driver: bridge
Assign services to specific networks:
services:
web:
image: nginx
networks:
- frontend
- backend
api:
image: node:22-alpine
networks:
- backend
db:
image: mysql:8.0
networks:
- backend
In this setup, the web service can reach both api and db because it belongs to both networks. The api and db services can communicate with each other on the backend network, but cannot directly access services that are only on the frontend network. For more details on Docker Compose networking, see the official networking documentation.
#Docker Compose volumes
Volumes persist data outside the container lifecycle. Without volumes, all data written to a container is lost when the container is removed.
#Named volumes vs. bind mounts
Docker Compose supports two types of volume mappings:
-
Named volumes are managed by Docker. You declare them in the top-level
volumessection of the Compose file. Docker stores the data in its own directory on the host (usually/var/lib/docker/volumes/). Named volumes perform better than bind mounts on macOS and Windows. -
Bind mounts map a specific host directory to a container path. The
./src:/var/www/htmlmapping in the LAMP stack example is a bind mount. Bind mounts work well for development because code changes on the host are reflected in the container immediately.
volumes:
mysql-data: # Named volume - Docker manages storage location
redis-data:
driver: local # Explicit driver (local is the default)
To list all Docker volumes on your system:
docker volume ls
To inspect a specific volume and see where Docker stores its data:
docker volume inspect my-demo-app_mysql-data
To remove unused volumes and free disk space:
docker volume prune
For a full overview of Docker storage options, see the Docker volumes documentation.
#Docker Compose Use Cases
-
Multiple container orchestration: Docker Compose lets you define applications with many containers in one YAML file. In a microservices architecture, you can deploy separate services for API, authentication, and data storage using one file. Each container is configured with its own network settings, environment variables, and volume mounts.
A single
docker compose up -dcommand replaces long scripts and manual container creation. For orchestration across multiple hosts, tools like Docker Swarm or Kubernetes handle multi-node deployments. -
Consistent development environments: Docker Compose ensures every developer uses the same containerized services. It defines the complete application stack in a YAML file managed by version control. The file includes databases, message brokers, and external services.
This simplifies dependency management, communication between services, and persistent storage. In addition, it creates smoother transitions from development to staging and production.
-
Staging environment replication: Docker Compose lets you replicate the exact configuration used in production by enabling you to define all the services (web servers, databases, and etc.) in a single YAML file.
A single command such as docker-compose up spins up the entire environment, reducing the time and effort required to build and deploy a complex multi-container application. This speed allows teams to quickly test changes and iterate on features without manual setup each time.
-
CI/CD pipeline integration: Many CI/CD systems (GitHub Actions, GitLab CI, Jenkins) support Docker Compose for spinning up test environments. A Compose file can start your application alongside a test database, run integration tests, and tear everything down when tests finish. The
docker compose runcommand executes one-off commands against a service, which is useful for running test suites or database migrations in CI.
#Troubleshooting
#"docker compose" command not found
The docker compose command (without a hyphen) requires the Compose V2 plugin. If you see "command not found," verify that the plugin binary exists at ~/.docker/cli-plugins/docker-compose and has execute permissions. Run chmod +x ~/.docker/cli-plugins/docker-compose to fix permission issues. Also, confirm that your Docker Engine version is 24.0 or later.
#Port already in use
If docker compose up fails with "address already in use," another process is occupying the port. Run sudo lsof -i :8080 (replace 8080 with the conflicting port) to identify the process. Stop that process or change the host port in your Compose file (e.g., from "8080:80" to "8081:80").
#Container exits immediately after starting
Check the container logs with docker compose logs <service-name>. Common causes include incorrect environment variables, missing configuration files, or permission errors on mounted volumes. For database containers, verify that all required environment variables (like MYSQL_ROOT_PASSWORD) are set.
#Changes to the Compose file do not take effect
After editing docker-compose.yml, you must run docker compose up -d again. Compose detects the configuration change and recreates only the affected containers. Running docker compose restart does not apply configuration changes because it only stops and starts the existing containers.
#Volumes not persisting data
Make sure you declared the volume in the top-level volumes section and referenced it correctly under the service. Bind mounts (using ./path:/container/path) require the host directory to exist. If you accidentally ran docker compose down -v, the named volumes were deleted along with all their data.
#Conclusion
You have learned how to install Docker Compose on Ubuntu 24.04, deploy a multi-container LAMP stack, manage container lifecycles, and work with essential features like custom networks, named volumes, and environment variable files.
Docker Compose reduces the complexity of running multi-container applications to a single YAML file and a few commands. Use docker compose up -d to start services, docker compose down to clean up, and docker compose pull followed by docker compose up -d to apply image updates.
FAQs
What is the difference between `docker compose` and `docker-compose`?
`docker-compose` (with a hyphen) is the legacy v1 binary written in Python. `docker compose` (without a hyphen) is the v2 plugin written in Go and bundled with Docker Engine. Docker Compose v1 reached end-of-life in June 2023. Use `docker compose` for all new projects.
Can I use Docker Compose in production?
Docker Compose works well for single-host production deployments, where you run all services on a single server. For multi-host setups that require load balancing, auto-scaling, and rolling updates, use an orchestration platform such as Kubernetes or Docker Swarm.
How do I update images in a Docker Compose project?
Run `docker compose pull` to download the latest versions of all images, then `docker compose up -d` to recreate containers with the updated images. Compose only restarts containers whose images have changed.
Is the `version` key still required in Compose files?
No. Docker Compose V2 and later ignore the `version` key and display a deprecation warning if it is present. The modern Compose Specification does not require it. Remove the `version` line from your Compose files to avoid the warning.
How do I view resource usage for Compose containers?
Run `docker compose top` to see running processes in each container, or `docker stats` to view real-time CPU, memory, and network usage for all running containers.
Can I run multiple Compose projects on the same server?
Yes. Each Compose project is isolated by its project name (which is derived from the directory name by default). Services in different projects do not share networks unless you explicitly configure an external network. Use the `-p` flag to set a custom project name: `docker compose -p my-project up -d`.
Starting at just $3.24 / month, get virtual servers with top-tier performance.
