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.