Docker compose is basically scripts which manage containers, allowing a developer to spin up or tear down multiple containers with a single command line. A compose file will run the same set of containers with the same configurations.

Compose files are written in yaml. Docker compose docs

Docker-compose is installed with "Docker for Windows" but on linux you have to install it seperatly. Check the version to see if it is installed.

> docker-compose --version

To install docker-compose on Linux run the below commands, except make sure the version is up to date on their github release page. it wont always be 1.22.0.

> sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

> sudo chmod +x /usr/local/bin/docker-compose

> docker-compose --version

Create a file named "docker-compose.yml" and add the following commands as you need.

versions

There are different formats for docker compose which is compatible for different docker engine versions. First check your docker engine version:

> docker --version

Second, check the compatibility matrix in the docker docs to see which version you need for your Docker engine.

Third specify your version at the top of your compose file.

version: '3.7'
collapse
adding apps/services/container

You may need more than one instance of a container therefore you dont set up containers, instead up "services". Add a name for each each service which will be used in the container name and specify the image it is made from.

When you need to use ":" for a value such as with and image:tag put it in quotes so that it doesn't interfere with the yaml structure.

version: '3.7'
services:
  myapp1:
    image: nginx
  myapp2:
    image: "nginx:alpine"

> docker-compose up -d

> docker ps

If you look at your running containers after docker-compose up, you will see the container names of "default_myapp1_1" and default_myapp2_1.

You do not have to "docker-compose down" everytime you make a change to the docker-compose.yml file, instead you can docker-compose up again. Docker will detect what changed and deploy that. Keep the original containers running and in the yaml file change 'image: nginx' to 'image: "nginx:alpine"'' and docker-compose up again.

version: '3.7'
services:
  myapp1:
    image: "nginx:alpine"
  myapp2:
    image: "nginx:alpine"

> docker-compose up -d

> docker ps

NOTE: If you look at the created times of the container you changed to the container you never changed, you will see one container was recently created and the other not.

collapse
Ports

Multiple ports can easily be exposed on each container.

version: '3.7'
services:
  myapp:
    image: "nginx:alpine"
    ports:
      - "80:80"
collapse
Volumes

Implicit Volumes and binds

Set up a volume with {volumeName}:{containerLocation}

version: '3.7'
services:
  myapp:
    image: "nginx:alpine"
    ports:
      - "80:80"
    volumes:
      - myVolume:/var/lib/data

# ... 

volumes:
  myVolume:

Below are different options for specifying volumes with the short syntax:

volumes:
# Just specify a path and let the Engine create a volume
- /var/lib/mysql

# Specify an absolute path mapping
- /opt/data:/var/lib/mysql

# Path on the host, relative to the Compose file
- ./cache:/tmp/cache

# User-relative path
- ~/configs:/etc/configs/:ro

# Named volume
- datavolume:/var/lib/mysql

Long Syntax

As of docker-compose v3.2 you can use long syntax which allows the configuration of additional fields that can be expressed in the short form such as mount type (volume, bind or tmpfs) and read_only.

Explicit Bind Mount

You can explicitly state the use of a bind mount with type:bind then adding the source(location on host) and the target(location in container).

version: '3.7'
services:
  myapp:
    image: "nginx:alpine"
    ports:
      - "80:80"
    volumes:
      - type: bind
        source: /myfolder/data
        target: /data

Explicit Volume Mount

You can explicitly state the use of a volume mount with type:volume then adding the source(name of the volume) and the target(location in container).

version: '3.7'
services:
  myapp:
    image: "nginx:alpine"
    ports:
      - "80:80"
    volumes:
      - type: volume
        source: volumeName
        target: /data

# ... 

volumes:
  volumeName:

NOTE: At the bottom, make sure to define the volumes used in this docker-compose file.

collapse
network

NOTE: networking requires docker-compose format version 2+

NOTE: docker-compose may create a new network which dont auto delete on docker-compose down. You can specify the details of these networks.

default network

If you do not specify a network, docker-compose will create and add all its containers to "default_default" bridge network.

version: '3.7'
services:
  myapp:
    image: nginx

> docker-compose up -d

> docker network ls

After you compose up containers and view the networks, you will see a new network was added "default_default"

Specify network

You can add each container to any number of specific networks Under your app specify the networks it needs to be part of. Then define all the networks you use in this compose file at the bottom.

version: '3.7'
 services:
   myapp:
     image: nginx
     networks:
       - mynet
 
 # ... 
 
 # define networks used
   networks:
     mynet:
 

> docker-compose up -d

> docker network ls

If you do not define the networks used you will get the following error:

Service "myapp" uses an undefined network "mynet"

When you run the above successfully you will notice a new network was added "default_mynet"

Existing networks

In the example below we create a network called "notcreatednet" which wont be created because it will use an already existing network called "existingnet". First create "existingnet":

> docker network create existingnet

version: '3.7'
services:
  myapp:
    image: nginx
    networks:
      - notcreatednet

# ... 

# define networks used
  networks:
    notcreatednet:
      external:
        name: existingnet

> docker-compose up -d

> docker network ls

You should notice in your list of networks that "notcreatednet" does not exist.

NOTE: If the network does not already ecist you will get an error "Network existingnet declared as external, but could not be found..."

collapse

Navigate to the folder which contains your docker-compose.yml file.

docker-compose up setsup volumes and networks required and starts all containers. Run the below command to run the compose file and start up all the containers.

> docker-compose up -d

docker-compose down stops all containers and removes them but keeps volumes and networks. You can add extra flags to remove the volumes or images.

> docker-compose down

> docker-compose down --help

> docker-compose down --rmi local

> docker-compose down -v

docker-compose up will run existing images and will build or fetch them if they not in docker. If you are developing locally then docker-compose build will rebuild your images.

> docker-compose build

MongoDb with express

Third specify your version at the top of your compose file.

version: '3.7'
services:

  mymongo:
    image: mongo:4
    restart: always
    ports:
      - 27018:27017
    volumes:
      - type: volume
        source: dbdata
        target: /data/db
    networks:
      - mynet
    environment:
      MONGO_INITDB_ROOT_USERNAME: adminuser
      MONGO_INITDB_ROOT_PASSWORD: mypassword
      # application connection string = mongodb://adminuser:[email protected]:27017/admin
      # other containers in the network will connect on port 27017
      # Connect to database from your local machine on port 27018

  mongo-express:
    image: mongo-express
    restart: always
    networks:
      - mynet
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_SERVER: mymongo
      ME_CONFIG_MONGODB_PORT: 27017
      ME_CONFIG_MONGODB_ADMINUSERNAME: adminuser
      ME_CONFIG_MONGODB_ADMINPASSWORD: mypassword
      # goto http://localhost:8081/

networks:
  mynet:

volumes:
  dbdata:
collapse

Check out these links for more info:

My Docker Samples