2025-11-02
1 min
3-2-1-1-0 Backup Automation
Matthew’s 3-2-1-1-0 Backup Automation Makefile
This is the full version of Matthew’s 3-2-1-1-0 Backup Automation Makefile .
It’s formatted for correct rendering in Codex / Zola Markdown without overflow.
include .env
DATE := $( shell date +% F_ % T )
RESTORE_DIR := /tmp/writing-restore-test
.PHONY : all git-save backup-local mirror-local backup-cloud backup-cloud-scaleway manifest verify verify-restore offline-copy cron-install full
all : git-save backup-local mirror-local backup-cloud backup-cloud-scaleway manifest verify
full : all verify-restore
git-save :
@ echo " [GIT] Auto-committing changes..."
cd $( SRC_DIR) && \
git add . && \
git commit - m " Auto backup $( DATE) " || echo " No changes to commit." && \
git push origin main || echo " Push skipped/offline."
backup-local :
@ echo " [LOCAL] Syncing to CF-Express card (encrypted restic sync)..."
rsync - avh -- delete -- exclude= " .git" $( SRC_DIR) / $( LOCAL_BACKUP) / && sync
mirror-local :
@ echo " [LOCAL] Creating unencrypted rsync mirror to CF-Express..."
mkdir - p $( LOCAL_BACKUP) /unencrypted
rsync - avh -- delete $( SRC_DIR) / $( LOCAL_BACKUP) /unencrypted/writing-latest/
@ echo " [LOCAL] Creating versioned timestamped copy..."
rsync - avh -- delete $( SRC_DIR) / $( LOCAL_BACKUP) /unencrypted/writing-$( DATE) /
sync
@ echo " [LOCAL] Unencrypted mirror complete: $( LOCAL_BACKUP) /unencrypted/writing-$( DATE) "
backup-cloud :
@ echo " [CLOUD] Backing up to Backblaze B2..."
. $( PWD) /.env ; \
RESTIC_PASSWORD_FILE = /etc/restic/restic_pass \
restic - r $$ RESTIC_REPOSITORY backup $$ SRC_DIR -- verbose && \
RESTIC_PASSWORD_FILE = /etc/restic/restic_pass \
restic - r $$ RESTIC_REPOSITORY forget -- keep-daily 7 -- keep-weekly 4 -- prune
backup-cloud-scaleway :
@ echo " [CLOUD] Backing up to Scaleway Paris..."
bash - c ' \
set -e; \
source "$( PWD) /.env"; \
export RESTIC_PASSWORD_FILE=/etc/restic/restic_pass; \
export AWS_ACCESS_KEY_ID="$$ AWS_ACCESS_KEY_ID_SCL"; \
export AWS_SECRET_ACCESS_KEY="$$ AWS_SECRET_ACCESS_KEY_SCL"; \
echo "[SCL] Repository: $$ RESTIC_REPOSITORY_SCL"; \
restic -r "$$ RESTIC_REPOSITORY_SCL" backup "$$ SRC_DIR" --verbose; \
restic -r "$$ RESTIC_REPOSITORY_SCL" forget --keep-daily 7 --keep-weekly 4 --prune; \
'
manifest :
@ echo " [MANIFEST] Generating SHA256 manifest..."
find $( SRC_DIR) - type f - print0 | xargs - 0 sha256sum > $( SRC_DIR) /MANIFEST-$( DATE) .sha256
cp $( SRC_DIR) /MANIFEST-$( DATE) .sha256 $( LOCAL_BACKUP) / || true
. $( PWD) /.env ; \
RESTIC_PASSWORD_FILE = /etc/restic/restic_pass \
restic - r $$ RESTIC_REPOSITORY backup $( SRC_DIR) /MANIFEST-$( DATE) .sha256
bash - c ' \
source "$( PWD) /.env"; \
export RESTIC_PASSWORD_FILE=/etc/restic/restic_pass; \
export AWS_ACCESS_KEY_ID="$$ AWS_ACCESS_KEY_ID_SCL"; \
export AWS_SECRET_ACCESS_KEY="$$ AWS_SECRET_ACCESS_KEY_SCL"; \
restic -r "$$ RESTIC_REPOSITORY_SCL" backup "$( SRC_DIR) /MANIFEST-$( DATE) .sha256"; \
'
verify :
@ echo " [VERIFY] Checking repository integrity..."
. /home/mjh/writing/.env ; \
echo " - Backblaze B2" ; \
restic - r $$ RESTIC_REPOSITORY check -- with-cache; \
echo " - Scaleway Paris" ; \
AWS_ACCESS_KEY_ID = $$ AWS_ACCESS_KEY_ID_SCL AWS_SECRET_ACCESS_KEY = $$ AWS_SECRET_ACCESS_KEY_SCL restic - r $$ RESTIC_REPOSITORY_SCL check -- with-cache
verify-restore :
@ echo " [VERIFY] Testing restore from both clouds..."
rm - rf $( RESTORE_DIR)
mkdir - p $( RESTORE_DIR)
. $( PWD) /.env ; \
RESTIC_PASSWORD_FILE = /etc/restic/restic_pass \
restic - r $$ RESTIC_REPOSITORY restore latest -- target $( RESTORE_DIR) /b2
bash - c ' \
source "$( PWD) /.env"; \
export RESTIC_PASSWORD_FILE=/etc/restic/restic_pass; \
export AWS_ACCESS_KEY_ID="$$ AWS_ACCESS_KEY_ID_SCL"; \
export AWS_SECRET_ACCESS_KEY="$$ AWS_SECRET_ACCESS_KEY_SCL"; \
restic -r "$$ RESTIC_REPOSITORY_SCL" restore latest --target "$( RESTORE_DIR) /scaleway"; \
'
@ echo " [VERIFY] Comparing manifests..."
@ if ls $( SRC_DIR) /MANIFEST-* .sha256 > /dev/null 2 >& 1 ; then \
LATEST_MANIFEST = $$( ls -t $( SRC_DIR) /MANIFEST-*.sha256 | head -n 1) ; \
echo " - Using manifest $$ LATEST_MANIFEST" ; \
cd $( RESTORE_DIR) /b2 && sha256sum - c $$ LATEST_MANIFEST || true ; \
cd $( RESTORE_DIR) /scaleway && sha256sum - c $$ LATEST_MANIFEST || true ; \
else \
echo " No manifest found, skipping file verification." ; \
fi
@ echo " [VERIFY] Restore verification complete."
offline-copy :
@ echo " [OFFLINE] Creating offline tarball..."
tar - czf /media/mjh/0000/writing-$( DATE) .tar.gz - C $( SRC_DIR) .
sync
@ echo " [OFFLINE] Safely eject the CF-Express card for cold storage."
cron-install :
@ echo " 0 */6 * * * cd $( PWD) && make full >> $( PWD) /backup.log 2>&1" | crontab -
@ echo " [CRON] Installed job (runs every 6 hours, full backup)"