Simple script for backups
Why another backup tool? Because most existing solutions are way too complicated. They offer a ton of shit like support for dozens of protocols, encryption, rotation policies, and worst of all - they store your data in their own format. Good luck restoring anything if you don't have the binary on hand
I wanted something dumb and simple. A tool that just does incremental backups and nothing else. No dependencies, no magic. Just plain tar.gz files that you can access on any Linux system with nothing but tar
This entire script is essentially a convenient interface to tar
chains - incremental backups without any bullshit
Usage: chains [OPTION]...
Options:
-h, --help This help
-m, --main-directory DIR Main program directory (default: ~/.chains)
-i, --incremental Make incremental backup (to the last chain)
-f, --full Make full backup (new chain)
-r, --recover [TIMESTAMP] Restore the chain to the specified timestamp
If TIMESTAMP omitted, recovers latest state
TIMESTAMP format: YYMMDDTHHMMSS (e.g. 250309T143045)
-w, --verify [TIMESTAMP] Verify integrity of chain up to specified timestamp
If TIMESTAMP omitted, verifies entire latest chain
TIMESTAMP format: YYMMDDTHHMMSS (e.g. 250309T143045)
-C, --directory DIR Change to DIR before any operation
-p, --print Print all backup chains structure
-v, --verbose Verbosely list files processed
Notes:
1. GNU TAR IS REQUIRED
2. Interrupting backup creation may leave incomplete archives
3. Always verify your backups can be restored
4. EXCLUDE BACKUP DIR FROM BACKUP
This script was written for back up moderately important data to an external drive. Here's how I typically use it:
Weekly full backup:
chains -fm $KINGSTON/chains/
Daily incremental backups (run as often as needed):
chains -im $KINGSTON/chains/
Verify backup integrity:
chains -wm $KINGSTON/chains/
Restore latest backup:
chains -rm $KINGSTON/chains/ -C ~/projects/temp/
Restore concrete backup using timestamp:
chains -r 260309T205505 -m $KINGSTON/chains/ -C ~/projects/temp/
Display all existing backup chains:
chains -pm $KINGSTON/chains/
Note: $KINGSTON is a variable defined in my
.bashrcpointing to the mount point of my external drive
Just download somewhere
wget https://raw.githubusercontent.com/laserattack/chains/refs/heads/master/chains
Make it executable
chmod +x chains
And use
./chains
Optionally, you can add the script to your PATH
When you run chains, it automatically checks and creates (if missing) the required directory structure in your main directory (default: ~/.chains or custom path with -m flag):
MAIN_DIR/
├── include_paths.txt # List paths to backup (one per line, absolute paths only)
├── exclude_patterns.txt # Patterns to exclude (tar format)
└── chains/ # All backups stored here
A chain is a sequence of backups (tar.gz archives), where the first backup is full and all subsequent backups are incremental (containing only changes since the previous backup).
To start a new chain, create a full backup with:
chains -fm /path/to/main/dir
chains will create a folder for the chain and place the first backup there:
MAIN_DIR/
├── chains # All backups stored here
│ └── since_260309T200625 # Chain directory
│ ├── [2.0G] 260309T200625.tar.gz # Full backup
│ └── incremental.snar # Metadata file for incremental backups
├── exclude_patterns.txt # Patterns to exclude (tar format)
└── include_paths.txt # List paths to backup (one per line, absolute paths only)
Once a chain exists, add incremental backups with:
chains -im /path/to/main/dir
After the first incremental backup, the chain folder will look like this:
MAIN_DIR/
├── chains # All backups stored here
│ └── since_260309T200625 # Chain directory
│ ├── [2.0G] 260309T200625.tar.gz # Full backup
│ ├── [1.2M] 260309T202739.tar.gz # First incremental backup
│ └── incremental.snar # Metadata
├── exclude_patterns.txt # Patterns to exclude (tar format)
└── include_paths.txt # List paths to backup (one per line, absolute paths only)
As you continue taking incremental backups, they are added to the same chain:
MAIN_DIR/
├── chains # All backups stored here
│ └── since_260309T200625 # Chain directory
│ ├── [2.0G] 260309T200625.tar.gz # Full backup
│ ├── [1.2M] 260309T202739.tar.gz # Incremental backup
│ ├── [1.2M] 260309T202747.tar.gz # Incremental backup
│ ├── [1.5M] 260309T205505.tar.gz # Incremental backup
│ ├── [1.4M] 260309T211003.tar.gz # Incremental backup
│ ├── [1.7M] 260309T220534.tar.gz # Incremental backup
│ ├── [1.2M] 260309T221151.tar.gz # Incremental backup
│ ├── [1.5M] 260310T131826.tar.gz # Incremental backup
│ ├── [1.5M] 260310T133139.tar.gz # Incremental backup
│ ├── [221M] 260310T133328.tar.gz # Incremental backup (lots of changes)
│ ├── [1.3M] 260310T133457.tar.gz # Incremental backup
│ ├── [1.4M] 260310T135812.tar.gz # Incremental backup
│ ├── [1.6M] 260310T153300.tar.gz # Incremental backup
│ ├── [1.3M] 260310T153454.tar.gz # Incremental backup
│ ├── [1.3M] 260310T153504.tar.gz # Incremental backup
│ ├── [1.3M] 260310T153509.tar.gz # Incremental backup
│ ├── [1.6M] 260310T154758.tar.gz # Incremental backup
│ ├── [1.9M] 260310T164949.tar.gz # Incremental backup
│ └── incremental.snar # Metadata
├── exclude_patterns.txt # Patterns to exclude (tar format)
└── include_paths.txt # List paths to backup (one per line, absolute paths only)
When you want to start a fresh chain (for example, to create a new full backup baseline), simply run:
chains -fm /path/to/main/dir
This creates a new timestamped folder (e.g., since_260310T170000) and places a new full backup there. All subsequent incremental backups with -i will be added to this newest chain.
To restore your data to a state corresponding to a specific backup in a chain, you need to extract all backups from the first (full) backup up to and including the target backup, extracting them all into the same directory. This applies the changes sequentially, recreating the exact state at that point in time.
You can do this manually, or use chains:
# Restore to the latest backup
chains -rm /path/to/main/dir -C /path/to/output/dir
# Restore to a specific timestamp
chains -r 260309T205505 -m /path/to/main/dir -C /path/to/output/dir
- Linux system
- Perl 5.10 or newer
- GNU tar
