I wrote this script to backup Linux servers running MySQL among other things in Docker on top of the ZFS filesystem. It does the following things in sequence:

  • locks all databases
  • snapshots all volumes
  • unlocks databases
  • rsyncs over the latest snapshot

It always creates the same snapshot because I use it in tandem with zfs-auto-snapshot so this is mostly for syncing the snapshots to my NAS which does its own snapshotting.

One requirement for this setup is to have password-less authentication in the form of public key authentication between the current user on the system to backup and the target system.

#!/bin/bash

# Stop on error
set -e

# Debugging
# set -v

# Docker containers running MySQL
MYSQL_CONTAINERS=(
  my_mysql_1
  my_other_mysql_1
)

TARGET_HOST=your-target-backup.horse
TARGET_USER=server-backup
TARGET_PORT=22
TARGET_PATH=/backup/of/your-server

# For snapshotting
ROOT_VOLUME=tank

SNAPSHOT_NAME=sync-backup

# Destroy the snapshots (fails on first run, so ignore status code)
/usr/sbin/zfs destroy -r [email protected]$SNAPSHOT_NAME || true

# Lock databases for backup
for cont in $MYSQL_CONTAINERS
do
  /usr/bin/docker exec $cont bash -c "MYSQL_PWD=\$MYSQL_ROOT_PASSWORD mysql -uroot -e \"flush tables with read lock;\""
done

# Snapshot
/usr/sbin/zfs snap -r [email protected]$SNAPSHOT_NAME

# Unlock databases
for cont in $MYSQL_CONTAINERS
do
  /usr/bin/docker exec $cont bash -c "MYSQL_PWD=\$MYSQL_ROOT_PASSWORD mysql -uroot -e \"unlock tables;\""
done

# List all volumes to backup
VOLUMES=$(/usr/sbin/zfs list -r -o mountpoint $ROOT_VOLUME | grep /)

# Iterate over all volumes to sync them
for vol in $VOLUMES
do
  # `remslash` is volume name where / is replaced with -
  remslash=$(echo $vol | cut -c 2- | sed 's#/#-#g')

  rsync -e "ssh -p $TARGET_PORT" --delete-after -az "$vol/.zfs/snapshot/$SNAPSHOT_NAME/" "[email protected]$TARGET_HOST:$TARGET_PATH/$remslash"
done

If you read through the code, you'll find that I am replacing the pool/volume/subvolume with pool-volume-subvolume to circumvent rsync's --delete-after. I am using rsync because my NAS is running btrfs instead of ZFS. If I was running the same filesystem on both systems, I could basically stream over the snapshots. A very cool feature, but I am happy with my Synology NAS and it does not support ZFS.