Bash script that creates a zstd-compressed tar archive of a directory and deletes backups older than 2 days.
Table of Contents#
1. Overview#
This script performs a daily directory backup by compressing the source into a single .tar.zst archive using parallel zstd (pzstd) at compression level 15. After creating the backup, it removes old backup files from the destination to prevent disk exhaustion.
The script is intended to be scheduled via cron (e.g., daily at 03:00).
2. Prerequisites#
- pzstd - parallel zstd compression (part of the
zstdpackage) - tar - GNU tar for archiving
- find - GNU findutils for cleanup
- Sufficient disk space at the backup destination
- Write permission to the backup destination directory
Install on Debian/Ubuntu:
sudo apt install zstd tarInstall on Arch Linux:
sudo pacman -S zstd tar3. Parameters#
Edit these variables at the top of the script before use:
| Variable | Description | Example |
|---|---|---|
backdest | Destination directory for backup archives | /mnt/Backup/destination |
backsource | Source directory to back up | /path/to/dir |
The retention period is controlled by the -mtime value in the find cleanup command (see How It Works).
4. The Script#
#!/bin/bash
# Backup variables
backdest="/mnt/Backup/destination"
backsource="/path/to/dir"
# Date label for backup filename (YYYY-MM-DD)
date=$(date "+%F")
# Create compressed archive using parallel zstd at level 15
tar -cv -I "pzstd -15" -f "$backdest/Something-$date.tar.zst" "$backsource"
# Delete backups with modification time older than 2 days
# -mtime +2 matches files modified MORE than 2*24 hours ago
find "$backdest/" -mtime +2 -delete5. Usage#
Manual Execution#
chmod +x /opt/scripts/BackupDir.sh
/opt/scripts/BackupDir.shCron Schedule#
Run daily at 03:00:
0 3 * * * /opt/scripts/BackupDir.sh >> /var/log/backup-dir.log 2>&1Dry Run#
Test what the cleanup would delete without actually removing files:
# Preview which files would be deleted
find /mnt/Backup/destination/ -mtime +2 -print6. How It Works#
Archive creation -
tar -cv -I "pzstd -15"creates a verbose tar archive and pipes it through parallel zstd at compression level 15 (high compression, slower). The filename includes the current date inYYYY-MM-DDformat, producing files likeSomething-2026-03-22.tar.zst.Cleanup -
find $backdest/ -mtime +2 -deleteremoves files with a modification time strictly greater than 2 days (48 hours) ago. This means:- Today's backup: kept
- Yesterday's backup: kept
- Day before yesterday's backup: kept (less than 48h old depending on timing)
- Older backups: deleted
To change the retention period, adjust the
-mtimevalue. For example,-mtime +6keeps roughly 7 days of backups.
Recommended Improvements#
For production use, consider adding:
#!/bin/bash
set -euo pipefail
backdest="/mnt/Backup/destination"
backsource="/path/to/dir"
date=$(date "+%F")
logfile="/var/log/backup-dir.log"
# Verify destination is mounted/accessible
if [[ ! -d "$backdest" ]]; then
echo "$(date '+%F %T') - ERROR: Backup destination not accessible: $backdest" >> "$logfile"
exit 1
fi
# Create archive
if tar -cv -I "pzstd -15" -f "$backdest/Something-$date.tar.zst" "$backsource" >> "$logfile" 2>&1; then
echo "$(date '+%F %T') - Backup completed successfully" >> "$logfile"
else
echo "$(date '+%F %T') - ERROR: Backup failed with exit code $?" >> "$logfile"
exit 1
fi
# Cleanup old backups
find "$backdest/" -name "*.tar.zst" -mtime +2 -delete
echo "$(date '+%F %T') - Cleanup completed" >> "$logfile"Key improvements:
set -euo pipefail- exit on errors, undefined variables, and pipe failures- Destination directory check before writing
- Error logging with timestamps
- Cleanup targets only
.tar.zstfiles (avoids deleting unrelated files)
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
pzstd: command not found | zstd package not installed | Install zstd package |
| Archive is 0 bytes | Source path does not exist or is empty | Verify backsource path |
| Old backups not deleted | -mtime threshold not reached | Check file modification times with stat |
| Disk full | Retention too long or source too large | Reduce -mtime value or lower compression level |
| Permission denied | User lacks write access to destination | Run as appropriate user or fix permissions |