Understanding Docker Compose With Examples

Last updated: December 2025

When applications grow beyond a single container, managing them with long docker run commands becomes difficult. Real-world applications are rarely made up of just one container. You often need a web server, a backend service, a database, maybe a cache, and all of them must communicate reliably. This is where Docker Compose shines.

Docker Compose allows you to define, configure, and run multi-container applications using a single YAML file. Instead of managing containers one by one, you describe the entire application stack declaratively and let Docker handle the rest.

What Is Docker Compose?

Docker Compose is a tool that lets you:

  • Define multiple containers (services)
  • Specify networks, volumes and environment variables.
  • Control startup order and dependencies
  • Run/start everything with a single command
  • Use a declarative YAML file instead of imperative CLI commands

Why Docker Compose Exists?

Without Docker Compose, running a simple app might look like this:

docker network create app-net
docker volume create db-data
docker run -d --name db --network app-net -v db-data:/var/lib/mysql mysql
docker run -d --name api --network app-net api-image
docker run -d -p 80:80 --name web --network app-net nginx
docker compose up -d

Docker Compose replaces imperative commands with a declarative configuration.

Core Concepts in Docker Compose

1. Services

A service represents a container (or group of identical containers) in your application.

Examples:

  • web
  • api
  • db

Each service maps directly to a container configuration.

2. Networks

Docker Compose automatically creates a bridge network for your application by default except specified otherwise.

Key benefits:

  • Containers can communicate using service names as hostnames
  • Networks are isolated per application

Example:

services:
  web:
    image: nginx
  api:
    image: my-api

3. Volumes

Volumes are used to persist data beyond the lifecycle of a container.

Typical use cases:

  • Databases
  • Uploaded files
  • Application state

Example:

services:
db:
image: mysql
volumes:
- db-data:/var/lib/mysql


volumes:
db-data:

Anatomy/Structure of a docker-compose.yml File

Here’s a simple example:

version: "3.9"            # This section specifies the Docker version

services:                 # This section specifies all the applications(containers) 

  web:                    # Application 1 name (frontend service)
    image: nginx:latest   # Docker image used for the web service
    ports:                # Exposes container ports to the host machine
      - "80:80"           # Host port 80 mapped to container port 80
    depends_on:           # Ensures the API service starts before this service
      - api

  api:                    # Application 2 name (backend service)
    image: my-api:latest  # Custom backend application image
    expose:               # Exposes port only within the Docker network
      - "8080"            # API listens on port 8080 internally

  db:                     # Application 3 name (database service)
    image: postgres:15    # PostgreSQL database image
    environment:          # Environment variables passed into the container
      POSTGRES_PASSWORD: secret   # Database password
    volumes:              # Mounts persistent storage into the container
      - db-data:/var/lib/postgresql/data  #Stores DB data outside the container

volumes:                  # This section defines volumes to be created 
  db-data:                # Docker-managed volume for database persistence

What This Does

  • Starts three containers: web, api, and db
  • Creates a private network for them
  • Exposes only the web service to the host
  • Persists database data using a volume

Running a Docker Compose Application

From the directory containing the compose file, run the command:

docker compose up -d

Useful commands:

docker compose ps # List running services
docker compose logs # View logs
docker compose stop # Stop services
docker compose down # Stop and remove containers
docker compose down -v # Remove containers and volumes

Other docker-compose.yaml file Examples

1. Basic Docker Compose Example (Single Service)

Here’s a simple Docker Compose file running an NGINX container:

a. create the compose file.

user1@LinuxSrv:~$ vi tekneed-website-compose.yaml

version: "3.9"
services:
  web8:
    image: nginx
    ports:
      - "8081:80"
    restart: always

b. Validate the file before running if you wish

user1@LinuxSrv:~$ docker compose -f tekneed-website-compose.yaml config

WARN[0000] /home/user1/tekneed-website-compose.yaml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
name: user1
services:
  web8:
    image: nginx
    networks:
      default: null
    ports:
      - mode: ingress
        target: 80
        published: "8081"
        protocol: tcp
    restart: always
networks:
  default:
    name: user1_default

c. Start the container

user1@LinuxSrv:~$ docker compose -f tekneed-website-compose.yaml up -d

WARN[0000] /home/user1/tekneed-website-compose.yaml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 2/2
 ✔ Network user1_default   Created                                                                                                                                                                                                      0.0s
 ✔ Container user1-web8-1  Started      

d. Verify that the container is up

user1@LinuxSrv:~$ docker compose -f tekneed-website-compose.yaml ps

WARN[0000] /home/user1/tekneed-website-compose.yaml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
NAME           IMAGE     COMMAND                  SERVICE   CREATED         STATUS         PORTS
user1-web8-1   nginx     "/docker-entrypoint.…"   web8      3 minutes ago   Up 3 minutes   0.0.0.0:8081->80/tcp

2. Multiple Containers with a Custom Network

a. create the compose file

user1@LinuxSrv:~$ vi compose.yml

version: "3.9"
services:
  web9:
    image: nginx
    ports:
      - "8081:80"
    restart: always
    networks:
      - web_network

  web10:
    image: nginx
    restart: always
    networks:
      web_network:
        ipv4_address: 10.10.10.22

networks:
  web_network:
    driver: bridge
    ipam:
      config:
        - subnet: "10.10.10.0/24"

b. Validate the file before running if you wish

user1@LinuxSrv:~$ docker compose config

WARN[0000] /home/user1/compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
name: user1
services:
  web9:
    image: nginx
    networks:
      web_network: null
    ports:
      - mode: ingress
        target: 80
        published: "8081"
        protocol: tcp
    restart: always

  web10:
    image: nginx
    networks:
      web_network:
        ipv4_address: 10.10.10.22
    restart: always

networks:
  web_network:
    name: user1_web_network
    driver: bridge
    ipam:
      config:
        - subnet: 10.10.10.0/24

c. Start the container

user1@LinuxSrv:~$ docker compose up -d

WARN[0000] /home/user1/compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 0/1
[+] Running 2/31_web_network  Creating                                                                                                                                                                                                  0.0s
 ✔ Network user1_web_network  Created                                                                                                                                                                                                   0.1s
 ⠏ Container user1-web9-1     Starting                                                                                                                                                                                                  0.9s
 ✔ Container user1-web10-1    Started                                                                                                                                                                                                   0.7s
Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint user1-web9-1 (667dcc42d22d12066e7a6c77e72da5ffc7b6b9c3eb4afcf13ee320d28d206c2f): Bind for 0.0.0.0:8081 failed: port is already allocated

You can see an error here saying that the port is already allocated. so let’s change the port to 8089 and re-create

user1@LinuxSrv:~$ docker compose up -d

WARN[0000] /home/user1/compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
WARN[0000] Found orphan containers ([user1-web8-1]) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
[+] Running 2/2
 ✔ Container user1-web10-1  Running                                                                                                                                                                                                     0.0s
 ✔ Container user1-web9-1   Started    

d. Verify that the container is up

user1@LinuxSrv:~$ docker compose ps

WARN[0000] /home/user1/compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
NAME            IMAGE     COMMAND                  SERVICE   CREATED          STATUS          PORTS
user1-web10-1   nginx     "/docker-entrypoint.…"   web10     4 minutes ago    Up 4 minutes    80/tcp
user1-web8-1    nginx     "/docker-entrypoint.…"   web8      35 minutes ago   Up 35 minutes   0.0.0.0:8081->80/tcp
user1-web9-1    nginx     "/docker-entrypoint.…"   web9      2 minutes ago    Up 2 minutes    0.0.0.0:8089->80/tcp

2. Two-Tier Application Using Docker Compose WordPress + MySQL (Network + Storage)

Having understood above steps, we will just create the compose file straighaway. I will rename the above file so that I can have a new default compose file.

user1@LinuxSrv:~$ mv compose.yml compose-nginx.yml

I will now create the file.

user1@LinuxSrv:~$ vi compose.yml

version: "3.9"

services:
  wordpress:
    image: wordpress
    ports:
      - "8088:80"
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: root
      WORDPRESS_DB_PASSWORD: CURLf
      WORDPRESS_DB_NAME: wordpress
    depends_on:
      - mysql
    networks:
      - app_network

  mysql:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_ROOT_PASSWORD: CURLf
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - app_network

networks:
  app_network:
    driver: bridge

volumes:
  mysql_data:

Start the containers

user1@LinuxSrv:~$ docker compose up -d

WARN[0000] /home/user1/compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 27/37
 ⠙ mysql [⣿⣿⣿⣀⣿⣿⣀⣿⣿⣿⣿] 65.76MB / 136.9MB Pulling                                                                                                                                                                                      123.2s                                                                                                                                                                                                   1.2s
   ✔ ffc89e9dfd88 Download complete                                                                                                                                                                                                     1.7s
   ⠹ 43d05e938198 Downloading     [==================>                                ]  20.97MB/56.29MB
[+] Running 2/2
[+] Running 4/41_app_network  Created                                                                                                                                                                                                   0.1s
 ✔ Network user1_app_network    Created                                                                                                                                                                                                 0.1s
 ✔ Volume user1_mysql_data      Created                                                                                                                                                                                                 0.0s
 ✔ Container user1-mysql-1      Started                                                                                                                                                                                                 2.8s
 ✔ Container user1-wordpress-1  Started                                                                                                                             

Verify that the container is up

user1@LinuxSrv:~$ docker compose ps

WARN[0000] /home/user1/compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
NAME                IMAGE       COMMAND                  SERVICE     CREATED          STATUS          PORTS
user1-mysql-1       mysql:5.7   "docker-entrypoint.s…"   mysql       5 minutes ago    Up 4 minutes    3306/tcp, 33060/tcp
user1-web10-1       nginx       "/docker-entrypoint.…"   web10       21 minutes ago   Up 21 minutes   80/tcp
user1-web8-1        nginx       "/docker-entrypoint.…"   web8        52 minutes ago   Up 52 minutes   0.0.0.0:8081->80/tcp
user1-web9-1        nginx       "/docker-entrypoint.…"   web9        19 minutes ago   Up 19 minutes   0.0.0.0:8089->80/tcp
user1-wordpress-1   wordpress   "docker-entrypoint.s…"   wordpress   4 minutes ago    Up 4 minutes    0.0.0.0:8088->80/tcp
docker compose

Conclusion

Docker Compose:

  • Simplifies multi-container applications
  • Uses a single declarative YAML file
  • Allows you to spin up entire environments with one command

Once you understand Docker Compose, moving to Kubernetes, Helm, OpenShift or Docker Swarm becomes much easier.

Be the first to comment

Leave a Reply

Your email address will not be published.


*