A Bash script to create and maintain the history of snapshots of Btrfs (sub)volumes.
Butter is from the "Btr" string in name "Btrfs", despite that it's actually "B-tree FS", not "Butter FS". Snap is for snapshot.
Features:
- Able to specify the path to store the snapshots.
- Can be inside or outside the target.
- Able to purge old snapshots when too many exist.
- Able to keep multiple snapshot schedules for the same target.
- Designed to be called from cron on a regular schedule.
- System: normally a GNU/Linux Distro.
- Dependencies:
- Bash.
- btrfs-progs, which provides
btrfscommand.
- AUR:
yay -S --needed --noconfirm butter-snap-git - Manually: Clone the repo, copy
buttersnapto/usr/local/bin/(or elsewhere included in$PATH) and ensure it has executing permission.
Makefile-based install :
This repository contains a simple Makefile that follows the common GNU conventions. By default it installs to /usr/local so local/manual installs won't conflict with distribution packages.
- To install locally (default PREFIX=/usr/local):
make install- To install into a staging directory (useful for packaging or testing):
make install DESTDIR=$(pwd)/staging
# resulting files will appear under staging/usr/local/bin and staging/usr/local/share/man/man1- To install for a distribution package (e.g. into
/usr), the packager typically overridesPREFIXand/or usesDESTDIR:
make install PREFIX=/usr DESTDIR=$pkgdirPKGBUILD example (Arch AUR packaging) :
An example snippet for an Arch PKGBUILD showing how a packager would install files into the package directory:
package() {
cd "$srcdir/$pkgname"
make install PREFIX=/usr DESTDIR="$pkgdir"
}The important part is supporting PREFIX and DESTDIR in the Makefile. PREFIX controls the final installation root (e.g. /usr vs /usr/local) and DESTDIR is used by packagers to stage files into a package directory — this is the standard and expected behavior for packaging systems.
Syntax:
# Options can be placed before target.
buttersnap <target> [options]The command above creates a snapshot of <target> as <store path>/<snapname>.
Example:
buttersnap / -n"hourly"will make a snapshot of/in/.snapshotswith a namehourly_<date>_<time>.
<date>is on the formYYYY-MM-DD,<time>is on the formHH:MM:SS,- The 5 newest snapshots matching the prefix
hourly_are kept around; the rest are deleted.
The arguments are detailed in the following sections.
<target>: The path or mountpoint of a Btrfs (sub)volume.
- Internally it will be read as
target=$(realpath -m <target>).
<store path>: The path where snapshots are stored.
- Determined by
<store directory>and<store pathtype>.
Related options:
-s,--store-dir <store directory>: Specify the value for<store directory>(by default.snapshots).-S,--store-pathtype <store pathtype>: Specify the value for<store pathtype>(by defaultrel).rel(relative): Let<store path>to be<target (in realpath)>/<store directory>.mim(mimic): Let<store path>to be<store directory>/<target (in realpath)>.abs(absolute): Let<store path>to be<store directory>.- In this case, choose
<snapname adj>wisely to avoid name conflicts.
- In this case, choose
<snapname>: The name of snapshots.
- Determined by
<snapname adj>,<snapname type>,<snapname pattern>and<snapname options>.
Related options:
-n,--snapname-adj <snapname adj>: To be used in the name of the snapshot, by default as prefix. The default value issnapshot.- It's recommended to set this value corresponding to the schedule, such as
hourly,daily,weeklyor1m,3h,1d,1w,3mo.
- It's recommended to set this value corresponding to the schedule, such as
-N,--snapname-type <snapname type>: Select a set of snapname and matching pattern. By defaultdefault.default: Snapname in form of<snapname adj>_%Y-%m-%d_%H:%M:%S, with corresponding matching pattern.- Applicable snapname option(s):
compatible,postfix.
- Applicable snapname option(s):
vfs: Use the Sambavfs_shadow_copysnapshot naming convention. Snapname in form of@GMT-%Y.%m.%d-%H.%M.%S, with corresponding matching pattern.custom: Custom snapname and matching pattern.--snapname-value <snapname>: Specify customed snapname.--snapname-pattern <snapname pattern>: Specify customed pattern to match the snapname.
-o,--snapname-ops <snapname options>: Additional options for a snapname type. By default none.- Only apply when applicable.
- Multiple options should be split by
,, e.g.compatible,postfix. compatible: Compatible snapshot names (i.e. no colons that confuse SAMBA/Windows clients).postfix: Use<snapname adj>as postfix instead of prefix. Might be usefull for chronological sorting.
-t,--time <time>: A snapshot will be created only if the newest already existing snapshot is older than<time>in seconds.- Unless
-Tis specified, no snapshot will be made if<target>has identical timestamp as the newest snapshot. The modification timestamps of the subvolumes/folders are used for comparison which might not work in some scenarios.
- Unless
-T,--use-transid: When-tis specified, no snapshot will be made if<target>has a lower or equal transition-id than newest snapshot.-r,--readonly: Create the snapshot as readonly (requires btrfs-tools > v0.20).-E,--no-omit: Treats omitting of snapshots (e.g. due to options-t/-T) as an error.
-k,--keep <number>: Sort the snapshots matching<snapname pattern>by usingls -drand keep the first<number>snapshots; the rest ones will be deleted.- 5 snapshots will be kept if this option is not specified.
-q,--quiet: Silent unless an error occurs. Such output is cron-compatible.-h,--help: Print help message and exit.-V,--version: Print version message and exit.--show-all-btrfs: Print current Btrfs mountpoints and unmounted Btrfs subvolumes in system.
Choose only one method below will be enough.
Suppose you have two Btrfs subvolumes mounted at / and /home.
In crontab, add these two lines:
*/5 * * * * buttersnap / -n5m -k12 -r
0 0 * * * buttersnap /home -n1d -k7 -r -S"mim" -s"/snapshots"The above cronjob will
- Every 5 minutes, create a read-only snapshot of
/with name prefix5m_keeping 12 generations in/.snapshots. - Every day, create a read-only snapshot of
/homewith name prefix1d_keeping 7 generations in/snapshots/home.
Suppose you have a Btrfs subvolume mounted at /data.
Find a way to run the commands below regularily (e.g. every 10 minutes).
buttersnap -r -S"abs" -s <store path> -t3600 /data -n"data_hourly" -k24
buttersnap -r -S"abs" -s <store path> -t$((3600*24)) /data -n"data_daily" -k7
buttersnap -r -S"abs" -s <store path> -t$((3600*24*7)) /data -n"data_weekly" -k4This program is distributed under the GNU General Public License.
The script is originally a rework of QDaniel/btrfs-snap, which is a fork of jf647/btrfs-snap.
- Developed by clsty.
- Originally by Birger Monsen [email protected]
- Readonly and basedir additions by James FitzGibbon [email protected]
- VFS snapshot naming support by gitmopp (https://github.com/gitmopp)
- Support for snapshotting unmounted btrfs subvolumes by Brian Kloppenborg (https://github.com/bkloppenborg)
- Add switches
-c(for windows-combitible timestamps) and-d(for specifying the snapshot directory) by Lukas Pirl ([email protected]) - Add switches
-B(absolute path of snapshot directory) and-t/-T(time-dependent snapshot) by Michael Walz ([email protected]) - Other improvements by chipturner, lpirl and QDaniel