What you will learn

Ship a backup script that archives a directory tree, records a checksum manifest, and applies a tiered retention policy (keep all dailies for a week, one weekly for a month, one monthly for a year) instead of a single flat cutoff.

Before you begin

You need tar, sha256sum, and enough scratch space for one full archive. Test restoration before trusting this in production — an unverified backup is a guess, not a backup.

1. Take a consistent, checksummed snapshot

#!/usr/bin/env bash
set -Eeuo pipefail

SRC_DIR="/opt/myapp/data"
VAULT_DIR="/backups/myapp"
STAMP="$(date -u +%Y%m%d-%H%M%S)"
ARCHIVE="${VAULT_DIR}/daily/myapp-${STAMP}.tar.gz"

mkdir -p "${VAULT_DIR}/daily" "${VAULT_DIR}/weekly" "${VAULT_DIR}/monthly"

tar -C "$(dirname "$SRC_DIR")" -czf "$ARCHIVE" "$(basename "$SRC_DIR")"
sha256sum "$ARCHIVE" > "${ARCHIVE}.sha256"

Running tar with -C and a relative basename keeps absolute paths out of the archive, so it can be restored under a different root later.

2. Promote copies into the weekly/monthly tiers

DOW="$(date -u +%u)"     # 1 = Monday
DOM="$(date -u +%d)"

if [[ "$DOW" == "7" ]]; then
    cp -- "$ARCHIVE" "${VAULT_DIR}/weekly/"
    cp -- "${ARCHIVE}.sha256" "${VAULT_DIR}/weekly/"
fi

if [[ "$DOM" == "01" ]]; then
    cp -- "$ARCHIVE" "${VAULT_DIR}/monthly/"
    cp -- "${ARCHIVE}.sha256" "${VAULT_DIR}/monthly/"
fi

Promotion is a plain cp, not a move — the daily tier still expires it on schedule below, independent of the copy living on in a coarser tier.

3. Expire each tier on its own horizon

prune_tier() {
    local dir="$1" days="$2"
    find "$dir" -maxdepth 1 -name '*.tar.gz*' -mtime +"$days" -delete
}

prune_tier "${VAULT_DIR}/daily"   7
prune_tier "${VAULT_DIR}/weekly"  31
prune_tier "${VAULT_DIR}/monthly" 366

echo "backup complete: $ARCHIVE"

Example output

backup complete: /backups/myapp/daily/myapp-20260919-030000.tar.gz

Verify the result

sha256sum -c myapp-20260919-030000.tar.gz.sha256 must report OK. Extract into a scratch directory and diff a sample of files against the source to catch silent tar corruption.

Troubleshooting

If the monthly tier never fills, confirm the cron job actually runs on the 1st in the timezone your date -u assumes — mixing local and UTC dates is the most common cause of tiers silently never promoting.

Recovery and next steps

Pair this with backup-restore-verification-job so retention isn't just "files exist" but "files restore cleanly," and route failures through webhook-alert-dispatcher.