Getting Started with Docker Compose

Introduction

I have been toying around with Docker for a few months now ever since I first bought a Raspberry Pi. It has been a great learning experience. Currently, I have over 20 self-hosted applications running in Docker. However, last week my power started to flicker and I began to wonder what would happen if my Raspberry Pi completely died?

My persistent data volumes for the containers are stored on a separate network file share in my NAS so I know they would be safe. What I would lose is all of my various environmental variables, port settings, and other miscellaneous settings that are included in my docker run codes. At the rate I am adding containers to my setup, it would take hours to recreate my environment. Then I discovered Docker Compose. Docker Compose allows me to store my docker environment in a single .yaml file and boot it up on any Docker host.

Basic Example

I presently use the docker run command to spin-up an application. It is simple, I just type the command in my terminal, hit enter, and in a few seconds my application is ready for access. The Gitea application container has a simple docker run code that I will use as an example:

docker run \
-p 3000:3000 \
-p 22:22 \
-v gitea:/data \
--restart always \
kunde21/gitea-arm:latest

I used Composerize to easily convert a standard docker run command to a format that I can place inside my docker-compose.yml file. In order to add this as a service in the file it would look like this:

version: '3.3'
services:
    gitea-arm:
        ports:
            - '3000:3000'
            - '22:22'
        volumes:
            - 'gitea:/data'
        restart: always
        image: 'kunde21/gitea-arm:latest'

As you can see, the above docker-compose.yml file contains the same port mappings, volumes, options, and image as the original docker run command.

Example with Variables

Next is an example of a docker run command for a container that has many environmental variables. It is the MariaDB container that is maintained by LinuxServer.io.

docker run \
  --name=mariadb \
  -e PUID=1000 \
  -e PGID=1000 \
  -e MYSQL_ROOT_PASSWORD=ROOT_ACCESS_PASSWORD \
  -e TZ=America/New_York \
  -e MYSQL_DATABASE=USER_DB_NAME \
  -e MYSQL_USER=MYSQL_USER  \
  -e MYSQL_PASSWORD=DATABASE_PASSWORD \
  -p 3306:3306 \
  -v path_to_data:/config \
  --restart unless-stopped \
  linuxserver/mariadb

After translating the above run command into a docker-compose.yml it comes out like this:

version: '3.3'
services:
    mariadb:
        container_name: mariadb
        environment:
            - PUID=1000
            - PGID=1000
            - MYSQL_ROOT_PASSWORD=ROOT_ACCESS_PASSWORD
            - TZ=America/New_York
            - MYSQL_DATABASE=USER_DB_NAME
            - MYSQL_USER=MYSQL_USER
            - MYSQL_PASSWORD=DATABASE_PASSWORD
        ports:
            - '3306:3306'
        volumes:
            - 'path_to_data:/config'
        restart: unless-stopped
        image: linuxserver/mariadb

Bringing it all together

I combined my services into a single docker-compose.yml file as outlined below. This file can be placed on a private GitHub repository or a self-hosted git repository. Keeping the file on GitHub allows me to edit it very easily. GitHub also serves as an offsite backup for the file.

version: '3.3'
services:
    gitea-arm:
        ports:
            - '3000:3000'
            - '22:22'
        volumes:
            - 'gitea:/data'
        restart: always
        image: 'kunde21/gitea-arm:latest'

    mariadb:
        container_name: mariadb
        environment:
            - PUID=1000
            - PGID=1000
            - MYSQL_ROOT_PASSWORD=ROOT_ACCESS_PASSWORD
            - TZ=America/New_York
            - MYSQL_DATABASE=USER_DB_NAME
            - MYSQL_USER=MYSQL_USER
            - MYSQL_PASSWORD=DATABASE_PASSWORD
        ports:
            - '3306:3306'
        volumes:
            - 'path_to_data:/config'
        restart: unless-stopped
        image: linuxserver/mariadb

With a master docker compose file, if I ever have to replace my docker host machine all I will have to do is install docker, upload this file onto the machine and run the docker-compose up command and all my applications will spin-up with their assigned port numbers. This will save so much time when compared to running each individual docker run command for all of my containers.