How to Periodically Backup Your Databases to Git

If you lost all the data in your databases, how would you feel? If you can't lose them, make sure you back them up at the right frequency!

4 min read
How to Periodically Backup Your Databases to Git
Photo by Arif Kemal Köroğlu from Pexels.

Context

Every week, I spend many hours on my blog to write a few articles that may be useful to the community or to a future me. Even though the value of my website continues to increase every time I create an article, I could have lost it completely if I had been unlucky until recently. 😨

Why? I hadn't taken to time to set a periodic backup of my databases. I haven't even activated the weekly backup of my droplet that DigitalOcean offers me for only $4.80 per month.

Last month I finally turned it on, but since it's weekly, I could still lose a week of work at most. It's better than two years, but it was still too much for me... 😕

It was time to find a better way to back up my databases periodically! 😎

Solution

There are probably many ways to save a database server, but in the end, I choose to save it as multiple SQL dumps in a Git repository.

Why choose Git instead of normal storage such as Dropbox, Microsoft OneDrive or Google Drive? Simply because it's free and comes with history! If something goes wrong during an update of one of the software I use, I can easily recreate a database with the previous data. 🧐

Are you ready to learn how I did it?

Step 1 - Set up a Git repository

Go to your favourite repository manager, GitLab or GitHub, and create a new empty Git repository.

As you probably already know them, I will not describe here the steps to create a new repository. 😉

Already done? Let's move on to the second step!

Step 2 - Create the directories

💡
Since my server only hosts applications with Docker Swarm, this procedure will be closely tied with this infrastructure. Feel free to adapt the rest of the procedure to your server. 😎

You need to decide where you are going to create the SQL dumps for each of your databases. Personally, I choose /var/opt/databases-backups on my server, but it could be anything.

And since my infrastructure currently has two types of database servers, MariaDB and MySQL, I'll also create two more directories to separate the databases in their place.

Let's create those directories!

mkdir -p /var/opt/databases-backups
mkdir -p /var/opt/databases-backups/mariadb
mkdir -p /var/opt/databases-backups/mysql

The root folder will serve as your Git repository, so let's initiate it!

cd /var/opt/databases-backups
git init --initial-branch=main
git remote add origin git@gitlab.com:benjaminrancourt/databases-backups.git

Step 3 - Create an SQL configuration file

As we want to easily manage access to our SQL servers, we will create an options file to store your credentials.

[mysqldump]
password=ThisIsThePassword
user=root
The content of my /var/opt/mariadb.cnf file.

Step 4 - Mount new Docker volumes

Before creating our script to back up our databases, we need to allow our containers to access the directories and the SQL option file we created earlier.

Let's mount them!

version: "3.8"

services:
  mariadb:
  	image: mariadb:10.8.3
    volumes:
      - /var/opt/databases-backups/mariadb:/databases-backups
      - /var/opt/mariadb:/var/lib/mysql
      - /var/opt/mariadb.cnf:/etc/my.cnf:ro
 
  mysql: 	
    image: mysql:8.0.29-debian
    volumes:
      - /var/opt/databases-backups/mysql:/databases-backups
      - /var/opt/mysql:/var/lib/mysql
      - /var/opt/mysql.cnf:/etc/my.cnf:ro
A snippet of my docker-compose.yml file with the new volumes mounted.

Step 5 - Create a Bash script

All the magic will be done in a special bash script. 🧙‍♂️

You can create the /var/opt/databases-backups/backup-data.sh file on your server with the below content.

It has been heavily commented on, so you should be able to understand it easily and make some adaptations if necessary. 😉

#!/bin/bash

# This script periodically saves databases dump to a GitLab repository that as
# been previously set up.

# To transfer this script to DigitalOcean server, use the following command:
# scp backup-data.sh USER@SERVER:/var/opt/databases-backups

# To install this script as a cron job:
# crontab -e
# */15 * * * * /var/opt/databases-backups/backup-data.sh

# List of MySQL servers that are in Docker
SERVERS=("mariadb" "mysql")

# Iterate on each server
for SERVER in "${SERVERS[@]}"; do
  echo "Beginning to dump databases for the ${SERVER} server..."

  # Find the container name to be able to run commands in it
  CONTAINER_NAME_PREFIX="phpmyadmin_${SERVER}"
  CONTAINER="$(docker ps --format='{{.Names}}' | grep "${CONTAINER_NAME_PREFIX}")"

  if [ -z "${CONTAINER}" ]; then
    echo "The container with name ${CONTAINER_NAME_PREFIX} was not found on this server ($(hostname))... :("
    continue
  fi

  # Obtain the list of databases names that we want to dump
  case "${SERVER}" in
    "mariadb")
      DATABASES=(
         "unami"
      )
    ;;

    "mysql")
      DATABASES=(
         "ghost"
      )
    ;;
  esac

  # Remove previous backup to not keep databases that we have removed from the script
  LOCAL_DIRECTORY="/var/opt/databases-backups/${SERVER}"
  echo "Removing old databases dumps (${LOCAL_DIRECTORY})..."
  rm -rf "${LOCAL_DIRECTORY:?}"/*

  # Iterate on each database
  for DATABASE in "${DATABASES[@]}"; do
    echo "Database dump for ${DATABASE} (${SERVER})..."

    # Create the command that is going to be executed in the container, where /databases-backups
    # is the directory inside the container of the volume we have mounted
    COMMAND="mysqldump ${DATABASE} \
      --quick \
      --single-transaction \
      --skip-dump-date \
      --skip-lock-tables \
      > /databases-backups/${DATABASE}.sql"

    # Execute the command inside the container
    docker exec "${CONTAINER}" sh -c "${COMMAND}"
  done

  echo "All databases has been dumped for ${SERVER}!"
  echo ""
done

# Go to the local directory which contains all databases backups
cd /var/opt/databases-backups || exit

# Add all changes to Git
git add .

# Commit them with the current date YYYY-MM-DD HH:MM
git commit -m "$(date +"%F %T") - Databases backups"

# Push the changes to GitLab
git push origin main

Once the script is created, don't forget to make it executable and test it!

# Make it executable
chmod +x backup-data.sh

# Test it by running it
./backup-data.sh

# Check your Git repository and make sure there is a new commit with the databases dumps

Step 6 - Create a new cronjob

Go to your server and type crontab -e to add a new cron.

# Connect to your server with ssh
ssh USER@SERVER

# Edit or create the cron file
crontab -e
0 3 * * * /usr/bin/docker system prune -f
*/15 * * * * /var/opt/databases-backups/backup-data.sh
The current crons I have on my server.
🧐
If you want to validate your cron schedule, you can use Crontab.guru, which explains exactly when your cron will run.

Conclusion

If you performed all the previous steps, your database should now be backup every 15 minutes! It's nice, isn't it? 😋

Do you have other ways to back up your databases? 😙