Managing multiple Docker containers manually can quickly become overwhelming. Docker Compose solves this by allowing you to define and run multi-container applications using a single YAML configuration file. This comprehensive guide walks you through installing Docker Compose and building your first multi-container application.
šÆ What You'll Learn: In this hands-on tutorial, you'll discover:
- How to install Docker Compose on Linux systems
- Understanding the docker-compose.yml file structure
- Defining multiple services (web server and database)
- Configuring volumes for data persistence
- Setting up custom networks for service communication
- Essential Docker Compose commands for managing applications
- Viewing logs and accessing running containers
š Why Docker Compose Matters
Docker Compose transforms complex multi-container deployments into simple, reproducible configurations. Instead of running multiple docker run
commands with dozens of flags, you define everything in a single file and launch your entire application stack with one command.
Prerequisites
Before we begin, make sure you have:
- Docker installed and running
- Basic understanding of Docker containers
- A Linux system (CentOS, RHEL, Fedora, or Ubuntu)
- Root or sudo privileges
- Internet connection for downloading packages
š¦ Step 1: Install Docker Compose
Docker Compose is now available as a Docker plugin, which means it integrates directly with the Docker CLI.
Download Docker Compose Binary
Download the latest version of Docker Compose from the official GitHub repository:
sudo curl -SL https://github.com/docker/compose/releases/download/v2.39.4/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
What this command does:
curl
: Downloads files from the internet-SL
: Shows errors (-S
) and follows redirects (-L
)-o /usr/local/bin/docker-compose
: Saves the file to the specified location
Expected Output:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 72.8M 100 72.8M 0 0 7015k 0 0:00:10 0:00:10 --:--:-- 7806k
This shows the download progress as it retrieves approximately 72.8 MB.
ā ļø Permission Error: If you see "Permission denied", make sure to use sudo
at the beginning of the command. Regular users don't have write access to /usr/local/bin/
.
Make the Binary Executable
Set execute permissions on the downloaded file:
sudo chmod +x /usr/local/bin/docker-compose
This command uses chmod +x
to add execute permission, allowing the file to run as a program.
Create a Symbolic Link (Optional)
For easier access, create a symbolic link in /usr/bin
:
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
Note: If you see File exists
error, the symlink is already created - this is fine.
Verify Installation
Check the installed Docker Compose version:
docker compose version
Expected Output:
Docker Compose version v2.39.2
ā
Installation Complete: You now have Docker Compose installed and ready to use. Note that the command is docker compose
(with a space) rather than the older docker-compose
(with a hyphen).
š Step 2: Understanding Docker Compose Help
View available Docker Compose commands and options:
docker compose --help
Key Output Sections:
Usage: docker compose [OPTIONS] COMMAND
Define and run multi-container applications with Docker
Options:
--env-file stringArray Specify an alternate environment file
-f, --file stringArray Compose configuration files
-p, --project-name string Project name
Commands:
build Build or rebuild services
config Parse, resolve and render compose file in canonical format
down Stop and remove containers, networks
exec Execute a command in a running container
logs View output from containers
ps List containers
pull Pull service images
restart Restart service containers
start Start services
stop Stop services
up Create and start containers
This gives you an overview of the most important commands you'll use to manage your applications.
šļø Step 3: Create Project Structure
Let's create a practical multi-container application with a web server and database.
Create Project Directory
mkdir docker-compose-practice
cd docker-compose-practice
Create Directory Structure
Set up the directory structure for our application:
mkdir -p web/html
mkdir -p database/init
The -p
flag creates parent directories if they don't exist.
View Directory Structure
Use the tree
command to visualize your directory structure:
tree
Output:
.
āāā database
ā āāā init
āāā web
āāā html
4 directories, 0 files
š Step 4: Create HTML File for Web Server
Create a simple HTML page that will be served by Nginx:
touch web/html/index.html
Edit the file with your preferred text editor and add the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Docker Compose Lab</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
background-color: #f4f4f4;
}
.container {
background-color: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
}
.info {
background-color: #e7f3ff;
padding: 15px;
border-left: 4px solid #2196F3;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to Docker Compose Lab!</h1>
<div class="info">
<h3>Multi-Container Application</h3>
<p>This web page is served by an Nginx container, which is part of a multi-container application managed by Docker Compose.</p>
<p><strong>Services Running:</strong></p>
<ul>
<li>Web Server: Nginx (Port 8080)</li>
<li>Database: MySQL (Port 3306)</li>
</ul>
</div>
<p>Congratulations! You have successfully deployed a multi-container application using Docker Compose.</p>
</div>
</body>
</html>
This creates a styled welcome page that confirms your multi-container setup is working.
šļø Step 5: Create Database Initialization Script
Create an SQL script that will run when the MySQL container starts:
touch database/init/init.sql
Add the following SQL content:
-- Create a sample database
CREATE DATABASE IF NOT EXISTS sampleapp;
-- Use the database
USE sampleapp;
-- Create a users table
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert sample data
INSERT INTO users (username, email) VALUES
('john_doe', 'john@example.com'),
('jane_smith', 'jane@example.com'),
('docker_user', 'docker@compose.com');
-- Create a simple view
CREATE VIEW user_count AS
SELECT COUNT(*) as total_users FROM users;
What this script does:
- Creates a database named
sampleapp
- Creates a
users
table with basic fields - Inserts three sample user records
- Creates a view to count total users
This file will be automatically executed when the MySQL container starts for the first time.
āļø Step 6: Create docker-compose.yml File
Now comes the most important part - the Docker Compose configuration file:
touch docker-compose.yml
Add the following configuration:
version: '3.8'
services:
webserver:
image: nginx:alpine
container_name: compose-webserver
ports:
- "8080:80"
volumes:
- ./web/html:/usr/share/nginx/html:ro
depends_on:
- database
networks:
- app-network
restart: unless-stopped
database:
image: mysql:8.0
container_name: compose-database
environment:
- MYSQL_ROOT_PASSWORD=rootpassword123
- MYSQL_DATABASE=sampleapp
- MYSQL_USER=appuser
- MYSQL_PASSWORD=apppassword123
ports:
- "3306:3306"
volumes:
- ./database/init:/docker-entrypoint-initdb.d:ro
- mysql-data:/var/lib/mysql
networks:
- app-network
restart: unless-stopped
volumes:
mysql-data:
driver: local
networks:
app-network:
driver: bridge
Understanding the Configuration
Section | Parameter | Purpose |
---|---|---|
version | '3.8' | Compose file format version (now optional) |
services | - | Defines all containers in your application |
webserver | image | Uses Nginx Alpine image (lightweight) |
webserver | ports | Maps host port 8080 to container port 80 |
webserver | volumes | Mounts local HTML files into container (read-only) |
webserver | depends_on | Ensures database starts before webserver |
database | environment | Sets MySQL configuration variables |
database | volumes | Mounts init scripts and persistent data storage |
networks | app-network | Creates isolated network for service communication |
restart | unless-stopped | Always restart containers except when manually stopped |
š” Version Note: The version
field is now considered obsolete in Docker Compose v2 and can be omitted. However, it's still commonly included for backwards compatibility.
š Step 7: Launch Your Multi-Container Application
Start all services defined in your docker-compose.yml file:
docker compose up -d
Command Breakdown:
up
: Creates and starts containers-d
: Runs in detached mode (background)
Expected Output:
[+] Running 4/4
ā Network docker-compose-practice_app-network Created 0.6s
ā Volume "docker-compose-practice_mysql-data" Created 0.0s
ā Container compose-database Started 1.1s
ā Container compose-webserver Started 1.8s
What happened:
- Docker Compose created a custom network called
docker-compose-practice_app-network
- Created a named volume
docker-compose-practice_mysql-data
for database persistence - Started the database container first (due to
depends_on
) - Started the webserver container
ā Application Running: Your multi-container application is now running! The webserver is accessible at http://localhost:8080 and the MySQL database is listening on port 3306.
š Step 8: View Container Logs
Monitor what's happening inside your containers:
View All Logs
docker compose logs
This displays logs from both the webserver and database containers.
View Logs for Specific Service
docker compose logs webserver
Expected Output (abbreviated):
compose-webserver | /docker-entrypoint.sh: Configuration complete; ready for start up
compose-webserver | 2025/10/02 14:13:31 [notice] 1#1: using the "epoll" event method
compose-webserver | 2025/10/02 14:13:31 [notice] 1#1: nginx/1.29.1
compose-webserver | 2025/10/02 14:13:31 [notice] 1#1: start worker processes
docker compose logs database
Expected Output (abbreviated):
compose-database | 2025-10-02 14:13:31+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.43-1.el9 started.
compose-database | 2025-10-02 14:13:32+00:00 [Note] [Entrypoint]: Initializing database files
compose-database | 2025-10-02 14:13:50+00:00 [Note] [Entrypoint]: Creating database sampleapp
compose-database | 2025-10-02 14:13:50+00:00 [Note] [Entrypoint]: Creating user appuser
compose-database | 2025-10-02 14:13:50+00:00 [Note] [Entrypoint]: running /docker-entrypoint-initdb.d/init.sql
compose-database | 2025-10-02T14:13:55.220344Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections.
Key observations from database logs:
- MySQL initialized the database files
- Created the
sampleapp
database - Created the
appuser
user - Executed our initialization script (
init.sql
) - MySQL is ready for connections on port 3306
Follow Logs in Real-Time
docker compose logs -f
The -f
flag follows the log output (similar to tail -f
). Press Ctrl+C
to stop following.
š Step 9: Check Running Containers
Using Docker Compose PS
docker compose ps
Expected Output:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
compose-database mysql:8.0 "docker-entrypoint.sā¦" database About a minute ago Up About a minute 0.0.0.0:3306->3306/tcp, 33060/tcp
compose-webserver nginx:alpine "/docker-entrypoint.ā¦" webserver About a minute ago Up About a minute 0.0.0.0:8080->80/tcp
Using Standard Docker PS
docker ps
Output:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3d96f6859c75 nginx:alpine "/docker-entrypoint.ā¦" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp compose-webserver
7588d5d02737 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 compose-database
Both commands show the running containers, but docker compose ps
is specific to your compose project.
š Step 10: Verify the Network
Check that Docker Compose created the custom network:
docker network ls
Expected Output:
NETWORK ID NAME DRIVER SCOPE
6c4642b06759 bridge bridge local
7b20c076257a docker-compose-practice_app-network bridge local
4014e1a64d4c host host local
b84fc03ca6b5 none null local
The docker-compose-practice_app-network
is automatically created and both services are connected to it, allowing them to communicate using service names.
š Step 11: Test the Web Application
Access your web application using curl:
curl http://localhost:8080
Expected Output (HTML):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Docker Compose Lab</title>
...
<h1>Welcome to Docker Compose Lab!</h1>
...
</html>
You can also open http://localhost:8080 in your web browser to see the styled page.
š¾ Step 12: Test the Database
Execute a MySQL query inside the running database container:
docker compose exec database mysql -u appuser -papppassword123 -e "SELECT * FROM sampleapp.users"
Command Breakdown:
exec database
: Execute command in database servicemysql
: Run MySQL client-u appuser
: Login as appuser-papppassword123
: Password (note: no space after -p)-e "..."
: Execute the SQL query
Expected Output:
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+-------------+--------------------+---------------------+
| id | username | email | created_at |
+----+-------------+--------------------+---------------------+
| 1 | john_doe | john@example.com | 2025-10-02 14:13:50 |
| 2 | jane_smith | jane@example.com | 2025-10-02 14:13:50 |
| 3 | docker_user | docker@compose.com | 2025-10-02 14:13:50 |
+----+-------------+--------------------+---------------------+
This confirms that:
- The database is running properly
- The initialization script executed successfully
- The sample data was inserted
- You can query the database from outside the container
ā ļø Security Note: Passing passwords on the command line is insecure for production use. This is fine for learning, but use environment files or secrets management in production.
šÆ Essential Docker Compose Commands Reference
Command | Purpose | Example |
---|---|---|
docker compose up -d | Start all services in background | Launch application |
docker compose down | Stop and remove containers, networks | Tear down application |
docker compose ps | List containers in project | Check status |
docker compose logs | View container logs | Debug issues |
docker compose logs -f | Follow logs in real-time | Monitor activity |
docker compose logs -t | Show timestamps in logs | Time-based debugging |
docker compose exec [service] [cmd] | Run command in service container | Execute MySQL queries |
docker compose stop | Stop services without removing | Pause application |
docker compose start | Start stopped services | Resume application |
docker compose restart | Restart all services | Apply config changes |
šÆ Key Takeaways
ā Remember These Points
- Single Configuration File: Docker Compose uses
docker-compose.yml
to define entire application stacks - Service Dependencies: Use
depends_on
to control startup order - Volume Persistence: Named volumes preserve data even when containers are removed
- Network Isolation: Compose creates custom networks for secure service communication
- Service Names: Containers can communicate using service names as hostnames
- Environment Variables: Configure applications using the
environment
section - Port Mapping: Map host ports to container ports for external access
š What's Next?
In Part 2, we'll cover advanced Docker Compose features:
- Scaling services horizontally
- Load balancing with Nginx
- Using custom configuration files
- Validating configurations with
docker compose config
- Managing volumes and cleanup strategies
- Override files for different environments
- Advanced troubleshooting techniques
š Further Reading
Official Resources
š Excellent Work! You've successfully installed Docker Compose and created your first multi-container application. You now understand how to define services, manage volumes, configure networks, and use essential compose commands.
Next Steps: Practice modifying the docker-compose.yml file, add more services, and get ready for Part 2 where we'll explore scaling and advanced features!
š¬ Discussion
Share your Docker Compose experience:
- What multi-container applications are you planning to build?
- Have you encountered any challenges setting up services?
- What other services would you like to see in a tutorial?
- How is Docker Compose improving your development workflow?
Connect with me: