A selective, timestamped file backup utility for GNU/Linux, written in Bash.
backup.sh started as a university operating systems assignment and was later improved into a more complete tool. It backs up files matching specified extensions from a source directory into a timestamped folder, compresses each file individually into a .tar.gz archive, optionally encrypts everything with GPG, writes a structured log, and cleans up old backup folders based on a retention policy.
It runs in two modes: interactive (prompts at runtime) and cron/silent (all parameters as arguments, no prompts).
This is a Bash scripting portfolio project, not a replacement for tools like borgbackup, restic, or rsync.
- Filter files by one or more extensions (e.g.
txt,pdf,jpg) - Timestamped backup directories — each run is isolated
- Per-file
.tar.gzcompression with post-creation integrity check (tar -tzf) - Discovered files manifest saved alongside archives
- Structured
backup.logwith timing, counts, and encryption status - Optional GPG symmetric encryption of all archives
- Configurable retention policy — removes old backup folders automatically
- Interactive mode with input validation
- Cron/silent mode with full argument validation before any files are written
- Meaningful exit codes for automation and cron monitoring
- Optional desktop notifications via
notify-send
| Requirement | Notes |
|---|---|
| GNU/Linux | Not compatible with macOS or BSD |
| Bash 4.0+ | Uses arrays and [[ ]] syntax |
| GNU coreutils | realpath, date, du |
| GNU findutils | find |
tar |
Compression and integrity checking |
gpg |
Optional — needed only if encryption is enabled |
notify-send |
Optional — desktop notifications on Linux |
git clone https://github.com/armilazarifi/bash-backup-util.git
cd bash-backup-util
chmod +x backup.shNo dependencies to install. The script runs directly.
./backup.sh <source_dir> <dest_dir>Prompts for extensions, retention days, and encryption choice.
./backup.sh <source_dir> <dest_dir> "<extensions>" <retention_days> <encrypt:y|n> "<passphrase>"All parameters passed as arguments. No prompts. Pass "" as the passphrase when encryption is off.
Arguments:
| Argument | Description |
|---|---|
source_dir |
Directory to back up (must exist) |
dest_dir |
Where timestamped backup folders are created |
extensions |
Space-separated extensions, e.g. "txt pdf jpg" |
retention_days |
Remove backups older than this many days (positive integer) |
encrypt |
Enable GPG encryption: y or n |
passphrase |
GPG passphrase — required when encrypt=y, otherwise pass "" |
Back up .txt and .pdf files, no encryption, 7-day retention:
./backup.sh ~/documents /mnt/backup "txt pdf" 7 n ""Back up .txt files with GPG encryption, 14-day retention:
./backup.sh ~/documents /mnt/backup "txt" 14 y "my_passphrase"Interactive mode:
./backup.sh ~/documents /mnt/backupCron job example (crontab -e):
0 2 * * * /home/user/bash-backup-util/backup.sh /home/user/documents /mnt/backup "txt pdf" 30 n "" >> ~/backup_cron.log 2>&1
See examples/cron_example.sh for a commented cron setup.
Each run creates a timestamped folder inside the destination directory.
Without encryption:
backup_2026_06_22_14_30_00/
├── discovered_files.txt
├── backup.log
├── notes_2026_06_22_14_30_00_1.tar.gz
├── report_2026_06_22_14_30_00_2.tar.gz
└── photo_2026_06_22_14_30_00_3.tar.gz
With GPG encryption — on successful encryption the original .tar.gz is removed, leaving only .tar.gz.gpg files:
backup_2026_06_22_14_30_00/
├── discovered_files.txt
├── backup.log
├── notes_2026_06_22_14_30_00_1.tar.gz.gpg
├── report_2026_06_22_14_30_00_2.tar.gz.gpg
└── photo_2026_06_22_14_30_00_3.tar.gz.gpg
If encryption fails for a specific archive, the original .tar.gz is kept so no data is lost.
discovered_files.txt — files found during the scan, saved before compression starts. Records discovery, not success — a file that disappears mid-run will appear here but be counted as failed in the log.
backup.log — written at the end of each run:
Backup Log
==========
Start time : 2026-06-22 14:30:00
End time : 2026-06-22 14:30:02
Duration : 2s
Source : /home/user/documents
Extensions : txt pdf
Backup dir : /mnt/backup/backup_2026_06_22_14_30_00
Backup size : 48K
Files found : 3
Files OK : 3
Files failed : 0
Encrypt requested : yes
Archives encrypted: 3
Encryption failed : 0
| Code | Meaning |
|---|---|
0 |
Full success |
1 |
Startup error — bad arguments, missing source, or gpg unavailable when encryption is required |
2 |
Compression failures only |
3 |
Encryption failures only |
4 |
Both compression and encryption failures |
Useful for monitoring cron jobs or chaining in shell scripts.
- GPG symmetric encryption is optional and off by default.
- The passphrase is passed to
gpgvia stdin (--passphrase-fd 0), not as a command-line argument, so it does not appear in the process list (ps). - However, in cron/silent mode the passphrase is a positional argument to the script itself, so it may appear in shell history or
/proc/*/cmdline. This is a known limitation of the current cron-mode interface. - If
gpgis missing and encryption is requested in cron mode, the script exits with code1before writing any files. It will never silently fall back to unencrypted output.
- GNU/Linux focused — relies on
date -dand other GNU-specific behavior that may not work on macOS or BSD. - Filenames with literal newlines are not supported by the manifest format.
- Per-file compression — each file gets its own
.tar.gzarchive. This makes individual file recovery straightforward, but the backup is not stored as one full-directory archive. - No advanced backup features — the script does not provide deduplication, incremental backups, or built-in remote backup targets like dedicated tools such as
borgbackup,restic, orrsync. - Retention uses
mtime— if a backup folder's modification time is changed externally, retention behavior may be affected.