backup-smartbotic-db.sh 2.2 KB

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