├── .gitignore ├── README.md ├── systemd ├── README.md ├── tarsnap-archive-daily.service ├── tarsnap-archive-daily.timer ├── tarsnap-archive-monthly.service ├── tarsnap-archive-monthly.timer ├── tarsnap-archive-prune.service ├── tarsnap-archive-prune.timer ├── tarsnap-archive-weekly.service └── tarsnap-archive-weekly.timer ├── tarsnap-archive.sh ├── tarsnap-cron.conf.sample ├── tarsnap-prune.sh └── tarsnap.crontab.sample /.gitignore: -------------------------------------------------------------------------------- 1 | tarsnap-cron.conf 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tarsnap-cron 2 | ============ 3 | 4 | Cron scripts (bash/shell) for tarsnap backup, including scheduled deletion of old backups. 5 | 6 | Description 7 | ----------- 8 | 9 | [tarsnap][tarsnap] is a very useful backup service, with lots of features, which I won't go into here. It allows separate roles, so you could have servers that cannot delete their own backups, even if they're hacked. 10 | 11 | These scripts attempt to make it easier for scheduled backups, with scheduled deletion of old backups. If you want to keep X daily backups, Y weekly backups and Z monthly backups, you can. 12 | 13 | Archiving and pruning can be run on different systems. The default weekly backups are on Mondays, and monthly backups are on the first. 14 | 15 | This is based on work by [Tim Bishop][bishop]. 16 | 17 | Some Linux distros are using systemd instead of cron. See the systemd folder for some relevant timers and documentation, contributed by @rphillips. 18 | 19 | 20 | Changes from the original 21 | ------------------------- 22 | 23 | I'm writing and testing this on Linux systems. It may work on FreeBSD, or not. You can try it, or just try Bishop's code. 24 | 25 | Note: I modified the format of the archive names. 26 | * an example from Bishop's script - 20140201-010100-monthly-/var/www 27 | * an example from my script - hostname-www-2014-02-01-0101-monthly 28 | 29 | I changed it to incorporate backups from a previous system of my design. The systems to prune old backups are incompatible, so if you switch between this script and his, old backups will stick around longer than they should. It would probably take manual cleanup to fix. 30 | 31 | I think this version is more extendible. Instead of reinventing cron, it simply uses a customized crontab. Hourly and yearly backups wouldn't be hard to implement, if there's any demand. 32 | 33 | [tarsnap]: http://www.tarsnap.com 34 | [bishop]: http://www.bishnet.net/tim/blog/2009/01/28/automating-tarsnap-backups/ 35 | -------------------------------------------------------------------------------- /systemd/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | 1. Copy tarsnap-archive.sh and -prune.sh files into /usr/local/bin 4 | 2. `chmod 755 /usr/local/bin/tarsnap-*` 5 | 3. Copy the systemd files into place: `cp *.timer *.service /etc/systemd/system` 6 | 4. run the following on each of the timer files (daily, weekly, monthly, and prune): 7 | 8 | ``` 9 | systemctl start /etc/systemd/system/{file}.timer 10 | systemctl enable /etc/systemd/system/{file}.timer 11 | ``` 12 | 13 | `systemctl start` begins the timer on this boot, and the `systemctl enable` 14 | will start the timer on a subsequent boot. 15 | 16 | ## Systemd Timers 17 | 18 | A good resource for learning more about systemd timers is 19 | [here](https://wiki.archlinux.org/index.php/Systemd/Timers). 20 | 21 | ## Testing 22 | 23 | Tested on Arch Linux, July 2015 24 | 25 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-daily.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tarsnap Archive Daily 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/bin/test $(date +\%u) -ne 1 && /usr/bin/test $(date +\%e) -ne 01 && /usr/local/bin/tarsnap-archive.sh daily 7 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-daily.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run tarsnap-archive Daily 3 | 4 | [Timer] 5 | OnCalendar=*-*-* 01:01:00 6 | Persistent=true 7 | Unit=tarsnap-archive-daily.service 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-monthly.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tarsnap Archive Monthly 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/local/bin/tarsnap-archive.sh monthly 7 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-monthly.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run tarsnap-archive Monthly 3 | 4 | [Timer] 5 | OnCalendar=*-*-1 01:01:00 6 | Persistent=true 7 | Unit=tarsnap-archive-monthly.service 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-prune.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tarsnap Prune Daily 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/local/bin/tarsnap-prune.sh 7 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-prune.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run tarsnap-prune 3 | 4 | [Timer] 5 | OnCalendar=*-*-* 01:55:00 6 | Persistent=true 7 | Unit=tarsnap-archive-prune.service 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-weekly.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tarsnap Archive Weekly 3 | 4 | [Service] 5 | Type=simple 6 | ExecStart=/usr/bin/test $(date +\%u) -eq 1 && /usr/bin/test $(date +\%e) -ne 01 && /usr/local/bin/tarsnap-archive.sh weekly 7 | -------------------------------------------------------------------------------- /systemd/tarsnap-archive-weekly.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run tarsnap-archive Weekly 3 | 4 | [Timer] 5 | OnCalendar=monday *-*-* 01:01 6 | Persistent=true 7 | Unit=tarsnap-archive-weekly.service 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /tarsnap-archive.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Tarsnap backup script 4 | # Written by Tim Bishop, 2009. 5 | # Modified by Pronoiac, 2014. 6 | 7 | # Directories to backup - set in 8 | CONFIG=/etc/tarsnap-cron.conf 9 | # CONFIG=tarsnap-cron.conf 10 | 11 | # note: this evals the config file, which can present a security issue 12 | # unless it's locked with the right permissions - e.g. not world-writable 13 | if [ ! -r "$CONFIG" ] ; then 14 | echo "ERROR: Couldn't read config file $CONFIG" 15 | echo Exiting now! 16 | exit 1 17 | fi 18 | 19 | source $CONFIG 20 | 21 | timestamp=`date +%Y-%m-%d-%H%M` 22 | 23 | if [ -z $1 ] 24 | then 25 | echo 1>&2 First parameter should be hourly, daily, weekly, monthly, as appropriate. 26 | exit 1 27 | fi 28 | 29 | # the last part of the suffix is now specified on the command line 30 | # cron should call this with daily, weekly, monthly, as appropriate 31 | # typical suffix: 2014-02-22-1920-daily 32 | if [ $# -ne 0 ] 33 | then 34 | SUFFIX=$timestamp-$1 35 | else 36 | SUFFIX=$timestamp 37 | fi 38 | 39 | # end of config 40 | 41 | 42 | # Do backups 43 | echo Starting backups. 44 | 45 | for BACKUP in "${BACKUP_ARRAY[@]}"; do 46 | SPLIT=(${BACKUP//=/ }) 47 | # typical label & path: www and /var/www 48 | label=${SPLIT[0]} 49 | path=${SPLIT[1]} 50 | echo "==> create $PREFIX-$label-$SUFFIX" 51 | cd $path && \ 52 | $TARSNAP $EXTRA_PARAMETERS -c -f $PREFIX-$label-$SUFFIX . 53 | done 54 | -------------------------------------------------------------------------------- /tarsnap-cron.conf.sample: -------------------------------------------------------------------------------- 1 | # shared between tarsnap-archive and tarsnap-prune 2 | 3 | # this file is executed as part of each of those. 4 | # it offers the capabilities of shell scripts. 5 | # this can present a security issue! this file should not be world-writable. 6 | 7 | # Path to tarsnap 8 | #TARSNAP="/home/tdb/tarsnap/tarsnap.pl" 9 | TARSNAP="/usr/local/bin/tarsnap" 10 | 11 | # DEBUG=true 12 | 13 | if [ "$DEBUG" = "true" ] 14 | then 15 | TARSNAP="echo tarsnap" 16 | fi 17 | 18 | # list of backups 19 | # format: label=path 20 | BACKUP_ARRAY=( 21 | home=/home 22 | etc=/etc 23 | www=/var/www 24 | ) 25 | 26 | PREFIX=`hostname` 27 | 28 | 29 | ## tarsnap-archive options 30 | 31 | BANDWIDTH=100000 32 | # bytes per second 33 | 34 | EXTRA_PARAMETERS="--maxbw-rate-up $BANDWIDTH" 35 | 36 | 37 | 38 | ## options for tarsnap-prune 39 | 40 | # Number of daily / weekly / monthly backups to keep 41 | DAILY=7 42 | WEEKLY=5 43 | MONTHLY=12 44 | HOURLY=24 45 | 46 | -------------------------------------------------------------------------------- /tarsnap-prune.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Tarsnap backup script 4 | # Written by Tim Bishop, 2009. 5 | # Modified by Pronoiac, 2014. 6 | 7 | # Directories to backup - set in 8 | CONFIG=/etc/tarsnap-cron.conf 9 | #CONFIG=tarsnap-cron.conf 10 | 11 | # note: this evals the config file, which can present a security issue 12 | # unless it's locked with the right permissions - e.g. not world-writable 13 | if [ ! -r "$CONFIG" ] ; then 14 | echo "ERROR: Couldn't read config file $CONFIG" 15 | echo Exiting now! 16 | exit 1 17 | fi 18 | 19 | source $CONFIG 20 | 21 | # end of config 22 | 23 | 24 | # using tail to find archives to delete, but its 25 | # +n syntax is out by one from what we want to do 26 | # (also +0 == +1, so we're safe :-) 27 | DAILY=`expr $DAILY + 1` 28 | WEEKLY=`expr $WEEKLY + 1` 29 | MONTHLY=`expr $MONTHLY + 1` 30 | HOURLY=`expr $HOURLY + 1` 31 | 32 | # Do deletes 33 | if [ "$DEBUG" = "true" ] 34 | then 35 | TMPFILE=/tmp/tarsnap.archives 36 | if [ ! -f $TMPFILE ]; then 37 | $TARSNAP --configfile /etc/tarsnap.conf --list-archives > $TMPFILE 38 | fi 39 | else 40 | TMPFILE=/tmp/tarsnap.archives.$$ 41 | $TARSNAP --configfile /etc/tarsnap.conf --list-archives > $TMPFILE 42 | fi 43 | 44 | DELARCHIVES="" 45 | 46 | for BACKUP in "${BACKUP_ARRAY[@]}"; do 47 | SPLIT=(${BACKUP//=/ }) 48 | # typical label & path: www and /var/www 49 | label=${SPLIT[0]} 50 | path=${SPLIT[1]} 51 | 52 | # hourly 53 | for i in `grep -E "^$PREFIX-$label-.+-hourly$" $TMPFILE | sort -rn | tail -n +$HOURLY`; do 54 | echo "==> delete $i" 55 | DELARCHIVES="$DELARCHIVES -f $i" 56 | done 57 | 58 | # daily 59 | for i in `grep -E "^$PREFIX-$label-.+-daily$" $TMPFILE | sort -rn | tail -n +$DAILY`; do 60 | echo "==> delete $i" 61 | DELARCHIVES="$DELARCHIVES -f $i" 62 | done 63 | 64 | # weekly 65 | for i in `grep -E "^$PREFIX-$label-.+-weekly$" $TMPFILE | sort -rn | tail -n +$WEEKLY`; do 66 | echo "==> delete $i" 67 | DELARCHIVES="$DELARCHIVES -f $i" 68 | done 69 | 70 | # monthly 71 | for i in `grep -E "^$PREFIX-$label-.+-monthly$" $TMPFILE | sort -rn | tail -n +$MONTHLY`; do 72 | echo "==> delete $i" 73 | DELARCHIVES="$DELARCHIVES -f $i" 74 | done 75 | 76 | done 77 | 78 | if [ X"$DELARCHIVES" != X ]; then 79 | echo "==> delete $DELARCHIVES" 80 | 81 | if [ "$DEBUG" = "true" ] 82 | then 83 | echo $TARSNAP -d $DELARCHIVES 84 | else 85 | $TARSNAP -d $DELARCHIVES 86 | rm $TMPFILE 87 | fi 88 | fi 89 | -------------------------------------------------------------------------------- /tarsnap.crontab.sample: -------------------------------------------------------------------------------- 1 | # this is set for backups at an arbitary 1:01am. 2 | 3 | # monthly - every 1st 4 | 1 1 1 * * /usr/local/bin/tarsnap-archive.sh monthly 5 | 6 | # weekly - every Monday, not on 1st 7 | 1 1 * * * test $(date +\%u) -eq 1 && test $(date +\%e) -ne 01 && /usr/local/bin/tarsnap-archive.sh weekly 8 | 9 | # daily - every day, except on 1st, or on Monday 10 | 1 1 * * * test $(date +\%u) -ne 1 && test $(date +\%e) -ne 01 && /usr/local/bin/tarsnap-archive.sh daily 11 | 12 | # hourly - 1 minute after the hour, except 1:01. 13 | # 1 0,2-23 * * * /usr/local/bin/tarsnap-archive.sh hourly 14 | 15 | * * * * * 16 | | | | | | 17 | | | | | +---- Day of the Week (range: 1-7) - date +%u, 1 standing for Monday 18 | | | | +------ Month of the Year (range: 1-12) 19 | | | +-------- Day of the Month (range: 1-31) - date +%e 20 | | +---------- Hour (range: 0-23) 21 | +------------ Minute (range: 0-59) 22 | --------------------------------------------------------------------------------