The script runs fine in my shell and does nothing from cron.
Cron runs with a minimal environment and no profile. Use absolute paths, set PATH at the top of the crontab, and redirect output to a file so the failure has somewhere to appear. Nine times in ten the missing piece is one of those three.
rsync of a directory with 2M small files takes forever.
The bottleneck is per-file syscalls, not bandwidth. tar piped over ssh moves them as a single stream and is often several times faster; rsync becomes the right tool again on the second run, when only the differences matter.
df says 96% full, du disagrees. What is going on?
Almost always a deleted file still held open by a process, so the space is gone but no path points at it. lsof +L1 lists them; restarting the holder releases the space instantly.