User:Rdeckard/Btrfs - Tips and tricks
Snapshots
Automatic snapshots on each boot
It is possible to automatically save the then current state of a btrfs subvolume during system boot. Two files are needed to accomplish this, a script which snapshots btrfs subvolumes and a systemd unit file to run the script during the boot process.
Requirements
The root filesystem must reside on its own subvolume with the toplevel subvolume as its parent, and the toplevel (id=5) subvolume must be accessible by mounting it at some mount point, for example at /run/btrfs-root
. In this example the layout is as follows:
subvolid=5 (/dev/sdaX, mounted as /run/btrfs-toplevel) | ├── @ (mounted as /) | | | ├── /bin (directory) | | | ├── /home (mounted @home subvolume) | | | ├── /usr (directory) | | | ├── /var/cache/pacman/pkg (nested subvolume) | | | ├── ... (other directories and nested subvolumes) | ├── @home (mounted as /home) | ├── @successful_boot (used in the script below) | └── @... (additional subvolumes you wish to use as mount points)
https://wiki.archlinux.org/index.php/Btrfs_-_Tips_and_tricks#Automatic_snapshots_on_each_boot
Snapshot script
An example script to snapshot all subvolumes is listed further below. The script will automatically detects subvolumes mounted or nested under / and creates snapshots of them under a directory specified by the caller.
The script deletes all snapshots from the state the system was in at last boot, makes new snapshots, and alters the /etc/fstab file of the snapshot of the root subvolume to allow it to be booted without manual configuration.
When called like this ...
# bash /usr/local/bin/snapshot_current_system_state.sh '/run/btrfs-toplevel' '@' '/run/btrfs/top-level/last_successful_boot'
the following structure will result:
# btrfs subvolume list '/'
ID 257 gen 1134 top level 5 path @ ID 258 gen 1135 top level 257 path @/var/cache/pacman/pkg ID 260 gen 1137 top level 5 path @home ID 277 gen 1128 top level 5 path @successful_boot/@ ID 280 gen 1130 top level 5 path @successful_boot/@home ID 281 gen 1131 top level 5 path @successful-boot/@/var/cache/pacman/pkg
Note that var, opt and home are subvolumes created by the caller in this example and mounted under '/'. The script detected and snapshotted them automatically. The resulting directory structure is:
/run/btrfs-toplevel/@ /run/btrfs-toplevel/@/var/cache/pacman/pkg /run/btrfs-toplevel/@home /run/btrfs-toplevel/@successful_boot/@ /run/btrfs-toplevel/@successful_boot/@/var/cache/pacman/pkg /run/btrfs-toplevel/@successful_boot/@home
The script below takes 3 parameters:
- The absolute path of the btrfs toplevel subvolume, for example,
/run/btrfs-toplevel
. - The name of the btrfs root subvolume as specified in /etc/fstab, for example,
@
, - The absolute path where the newly created snapshots will reside under the btrfs toplevel, for example
/run/btrfs-toplevel/@successful_boot
.
/run/btrfs-toplevel/
as the 3rd parameter would be incorrect and lead to data loss./usr/local/bin/snapshot_current_system_state.sh
#!/bin/bash # example call: # ./snapshot_current_system_state '/run/btrfs-toplevel' '@' '/run/btrfs-toplevel/successful_boot' if [[ $EUID != 0 ]]; then echo "This script must be run as root." exit 0 fi if [[ $# -ne 3 ]]; then echo "Usage:" echo echo " $(basename $0) toplevel_path root_subvol_name output_path" echo echo -e " toplevel_path\tThe path of the mounted toplevel subvolume. For example, '/run/btrfs-toplevel'." echo -e " root_subvol_name\tThe name of the btrfs root volume as specified in /etc/fstab. For exmaple, '@'." echo -e " output_path\t\tAbsolute path where the newly created snapshots will reside. For example, '/run/btrfs-toplevel/successful_boot'." echo echo " CAUTION: This script will delete all snapshots of the same name in 'output_path'." exit 0 fi declare -r btrfs_toplevel=$(readlink -f $1); shift declare -r root_subvolume_path=$1; shift declare -r output_path=$(readlink -f $1) if [[ "$btrfs_toplevel" == "$output_path" ]]; then echo "ERROR! You have set the output directory to be the same as your toplevel btrfs directory. Refusing to overwrite your toplevel subvolumes!" exit 1 fi exit 0 if [[ -e "/SNAPSHOT-TIMESTAMP" ]]; then echo "You're booted into a previous snapshot. Refusing to take snapshots." exit 0 fi # anti recursive snapshots for subvolume in $(btrfs subvolume list '/' | awk '{print $NF}'); do path_to_snapshot="$output_path/$subvolume" [[ -d "${path_to_snapshot}" ]] && btrfs subvolume delete "${path_to_snapshot}" done subvolumes="$(btrfs subvolume list '/' | awk '{print $NF}')" # rescan for subvolume in $subvolumes; do snapshot_directory="$output_path/$subvolume" [[ ! -d "$snapshot_directory" ]] && mkdir -p "$snapshot_directory" btrfs subvolume snapshot "$btrfs_toplevel/$subvolume" "$snapshot_directory" if [[ "$subvolume" == "$root_subvolume_path" ]]; then timestamp="$(date +%d.%m.%Y-%H:%M:%S)" echo -e "Arch Linux --- state at last successful boot (nonpersistent) [$timestamp]\n" > "$output_path/$root_subvolume_path/etc/issue" echo "$timestamp" > "$output_path/$root_subvolume_path/SNAPSHOT-TIMESTAMP" sed_output_path="$(echo $output_path | sed --posix --regexp-extended 's/\//\\\//g')" for subvolumeX in $(echo $subvolumes | /usr/bin/sed --posix --regexp-extended 's/\//\\\//g'); do sed --posix --regexp-extended "s/subvol=$subvolumeX/subvol=$sed_output_path\/$subvolumeX/g" --in-place "$output_path/$root_subvolume_path/etc/fstab" done fi done sync
Systemd unit file
The following systemd unit file will run the script every time the system manages to successfully boot into multi-user.target:
[Unit] Description=Takes a snapshot of each btrfs subvolume mounted under / after multi-user.target has been reached. After=multi-user.target [Service] Type=oneshot ExecStart=/usr/local/bin/snapshot_current_system_state '/run/btrfs-toplevel' '@' '/run/btrfs-toplevel/@successful_boot' [Install] WantedBy=multi-user.target
Enable snapshot_current_system_state_upon_boot.service
to have the script run on startup.
Booting into snapshots
In order to boot into a subvolume the rootflags=subvol=
option has to be used on the kernel line. The subvol=
mount options in /etc/fstab
of the snapshot to boot into also have to be specified correctly.
GRUB
You can manually create a GRUB#GNU/Linux menu entry with the rootflags=subvol=
argument. Alternatively, you can automatically populate your GRUB menu with btrfs snapshots when regenerating the GRUB configuration file by using grub-btrfsAUR or grub-btrfs-gitAUR.