| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- #!/bin/sh
- # Back up the SmartBotic database running in Docker on zeus.
- #
- # The database is stopped for the copy. A live copy of a WAL-backed store can be
- # torn, and a backup nobody can restore is worse than no backup because it is
- # believed in. The stop is seconds; the container comes back whatever happens,
- # including if this script dies partway.
- #
- # The encryption key lives inside the data directory. It is copied with
- # everything else and must never be separated from it - data without the key is
- # unreadable, and so is a backup without it.
- set -eu
- CONTAINER=smartbotic-db
- DATA=/data/smartbotic-db/data
- DEST=/data/backups/smartbotic-db
- KEEP=14
- STAMP=$(date +%Y%m%d-%H%M%S)
- ARCHIVE="$DEST/smartbotic-db-$STAMP.tar.zst"
- mkdir -p "$DEST"
- was_running=0
- if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null)" = "true" ]; then
- was_running=1
- fi
- restart_if_needed() {
- if [ "$was_running" = "1" ]; then
- docker start "$CONTAINER" >/dev/null 2>&1 || true
- fi
- }
- trap restart_if_needed EXIT INT TERM
- if [ "$was_running" = "1" ]; then
- docker stop "$CONTAINER" >/dev/null
- fi
- tar -C "$DATA" -cf - . | zstd -q -3 -o "$ARCHIVE"
- # Started again before the verification, so the database is down only for the
- # copy itself rather than for the reading back as well.
- restart_if_needed
- trap - EXIT INT TERM
- # A backup is not a backup until it has been read back. This checks the archive
- # is intact and that the encryption key is inside it - the one file whose
- # absence would not be noticed until a restore was already needed.
- if ! zstd -qt "$ARCHIVE"; then
- echo "BACKUP FAILED: $ARCHIVE does not decompress" >&2
- rm -f "$ARCHIVE"
- exit 1
- fi
- if ! zstd -qdc "$ARCHIVE" | tar -t | grep -q '^\./storage\.key$'; then
- echo "BACKUP FAILED: $ARCHIVE has no storage.key - the data would be unreadable" >&2
- rm -f "$ARCHIVE"
- exit 1
- fi
- # Only prune once a good one exists, so a run of failures cannot age out the
- # last working copy.
- ls -1t "$DEST"/smartbotic-db-*.tar.zst 2>/dev/null | tail -n +$((KEEP + 1)) | while read -r old; do
- rm -f "$old"
- done
- echo "$(date -Is) ok $ARCHIVE $(du -h "$ARCHIVE" | cut -f1) ($(ls -1 "$DEST"/smartbotic-db-*.tar.zst | wc -l) kept)"
|