diff --git a/ChangeLog b/ChangeLog index 97d0ebd..729fee0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,10 @@ btrbk-current * Set PATH variable instead of using absolute "/sbin/btrfs" for compatibility with all linux distros out there, which all install 'btrfs' in different locations (closes: #20). + * Added configuration option "snapshot_create", replacing option + "snapshot_create_always". This allows setups with multiple btrbk + instances on several hosts (closes: #18). + * Added command line option -r (resume only). * Catch and display errors from "btrfs subvolume show". * Include systemd service and timer unit for daily backups. diff --git a/README.md b/README.md index c8f2519..3bc709d 100644 --- a/README.md +++ b/README.md @@ -156,11 +156,10 @@ Retention policy: - `/mnt/btr_backup/mylaptop/rootfs.YYYYMMDD` - `/mnt/btr_backup/mylaptop/home.YYYYMMDD` -If you want the snapshots to be created even if the backup disk is not -attached (when you're on the road), simply add the following line to -the config: +If you want the snapshots to be created only if the backup disk is +attached, simply add the following line to the config: - snapshot_create_always yes + snapshot_create ondemand Example: host-initiated backup on fileserver @@ -215,19 +214,51 @@ This will pull backups from alpha/beta.mydomain.com and locally create: Example: local time-machine (daily snapshots) --------------------------------------------- -If all you want is a local time-machine of your home directory: +If all you want is creating snapshots of your home directory on a +regular basis: -/etc/btrbk/btrbk-timemachine.conf: +/etc/btrbk/btrbk.conf: volume /mnt/btr_pool + snapshot_dir btrbk_snapshots subvolume home - snapshot_dir btrbk_snapshots - snapshot_create_always yes /etc/cron.daily/btrbk: #!/bin/bash - /usr/sbin/btrbk -c /etc/btrbk/btrbk-timemachine.conf run + /usr/sbin/btrbk run + +Note that you can run btrbk more than once a day, e.g. by creating the +above script in `/etc/cron.hourly/btrbk`, or by calling `sudo btrbk +run` from the command line. + + +Example: multiple btrbk instances +--------------------------------- + +Let's say we have a host (at 192.168.0.42) running btrbk with the +setup of the time-machine example above, and we need a backup server +to only fetch the snapshots. + +/etc/btrbk/btrbk.conf (on backup server): + + volume ssh://192.168.0.42/mnt/btr_pool + subvolume home + snapshot_dir btrbk_snapshots + snapshot_preserve_daily all + snapshot_create no + resume_missing yes + + target_preserve_daily 0 + target_preserve_weekly 10 + target_preserve_monthly all + + target send-receive /mnt/btr_backup/my-laptop.com + +If the server runs btrbk with this config, the latest snapshot (which +is *always* transferred) as well as 10 weeklies and all monthlies are +received from 192.168.0.42. The source filesystem is never altered +because of `snapshot_preserve_daily all`. Setting up SSH diff --git a/btrbk b/btrbk index 5da8105..63766c3 100755 --- a/btrbk +++ b/btrbk @@ -47,7 +47,7 @@ use Date::Calc qw(Today Delta_Days Day_of_Week); use Getopt::Std; use Data::Dumper; -our $VERSION = "0.17.2-dev"; +our $VERSION = "0.18.0-dev"; our $AUTHOR = 'Axel Burri '; our $PROJECT_HOME = ''; @@ -62,9 +62,8 @@ my %config_options = ( # NOTE: keys "volume", "subvolume" and "target" are hardcoded snapshot_dir => { default => undef, accept_file => { relative => 1 } }, snapshot_name => { default => undef, accept_file => { name_only => 1 }, context => [ "subvolume" ] }, - receive_log => { default => undef, accept => [ "sidecar", "no" ], accept_file => { absolute => 1 }, deprecated => "removed" }, + snapshot_create => { default => "always", accept => [ "no", "always", "ondemand" ] }, incremental => { default => "yes", accept => [ "yes", "no", "strict" ] }, - snapshot_create_always => { default => undef, accept => [ "yes", "no" ] }, resume_missing => { default => "yes", accept => [ "yes", "no" ] }, preserve_day_of_week => { default => "sunday", accept => [ (keys %day_of_week_map) ] }, snapshot_preserve_daily => { default => "all", accept => [ "all" ], accept_numeric => 1 }, @@ -77,6 +76,22 @@ my %config_options = ( ssh_identity => { default => undef, accept_file => { absolute => 1 } }, ssh_user => { default => "root", accept_regexp => qr/^[a-z_][a-z0-9_-]*$/ }, btrfs_progs_compat => { default => undef, accept => [ "yes", "no" ] }, + + # deprecated options + snapshot_create_always => { default => undef, accept => [ "yes", "no" ], + deprecated => { yes => { warn => "Please use \"snapshot_create always\"", + replace_key => "snapshot_create", + replace_value => "always", + }, + no => { warn => "Please use \"snapshot_create no\" or \"snapshot_create ondemand\"", + replace_key => "snapshot_create", + replace_value => "ondemand", + } + }, + }, + receive_log => { default => undef, accept => [ "sidecar", "no" ], accept_file => { absolute => 1 }, + deprecated => { DEFAULT => { warn => "ignoring" } }, + } ); my @config_target_types = qw(send-receive); @@ -118,6 +133,7 @@ sub HELP_MESSAGE print STDERR " --version display version information\n"; print STDERR " -c FILE specify configuration file\n"; print STDERR " -p preserve all backups (do not delete any old targets)\n"; + print STDERR " -r resume only (no new snapshots, resume all missing backups)\n"; print STDERR " -v be verbose (set loglevel=info)\n"; print STDERR " -q be quiet (do not print summary at end of \"run\" command)\n"; print STDERR " -l LEVEL set loglevel (warn, info, debug, trace)\n"; @@ -322,8 +338,8 @@ sub config_key($$) my $key = shift || die; TRACE "config_key: context=$node->{CONTEXT}, key=$key"; while(not exists($node->{$key})) { - return undef unless($node->{PARENT}); - $node = $node->{PARENT}; + # note: all config keys exist in root context (at least with default values) + $node = $node->{PARENT} || die; } TRACE "config_key: found value=" . ($node->{$key} // ""); return $node->{$key}; @@ -394,6 +410,7 @@ sub parse_config(@) my $cur = $root; # set defaults foreach (keys %config_options) { + next if $config_options{$_}->{deprecated}; # don't pollute hash with deprecated options $root->{$_} = $config_options{$_}->{default}; } @@ -529,13 +546,21 @@ sub parse_config(@) return undef; } + if($config_options{$key}->{deprecated}) { + WARN "Found deprecated option \"$key $value\" in \"$file\" line $.: " . + ($config_options{$key}->{deprecated}->{$value}->{warn} // $config_options{$key}->{deprecated}->{DEFAULT}->{warn}); + my $replace_key = $config_options{$key}->{deprecated}->{$value}->{replace_key}; + my $replace_value = $config_options{$key}->{deprecated}->{$value}->{replace_value}; + if(defined($replace_key)) { + $key = $replace_key; + $value = $replace_value; + WARN "Using \"$key $value\""; + } + } + TRACE "config: adding option \"$key=$value\" to $cur->{CONTEXT} context"; $value = undef if($value eq "no"); # we don't want to check for "no" all the time $cur->{$key} = $value; - - if($config_options{$key}->{deprecated}) { - WARN "Found deprecated configuration option \"$key\" in \"$file\" line $."; - } } else { @@ -1187,6 +1212,7 @@ sub schedule(@) my $preserve_daily = $args{preserve_daily} // die; my $preserve_weekly = $args{preserve_weekly} // die; my $preserve_monthly = $args{preserve_monthly} // die; + my $preserve_latest = $args{preserve_latest} || 0; my $log_verbose = $args{log_verbose}; if($log_verbose) { @@ -1222,6 +1248,11 @@ sub schedule(@) } } + if($preserve_latest && (scalar @sorted_schedule)) { + my $href = $sorted_schedule[-1]; + $href->{preserve} ||= "preserve forced: latest in list"; + } + # filter daily, weekly, monthly my %first_in_delta_weeks; my %last_weekly_in_delta_months; @@ -1277,7 +1308,7 @@ MAIN: my @today = Today(); my %opts; - unless(getopts('hc:vql:p', \%opts)) { + unless(getopts('hc:prvql:', \%opts)) { VERSION_MESSAGE(); HELP_MESSAGE(0); exit 1; @@ -1297,6 +1328,7 @@ MAIN: @config_src = ( $opts{c} ) if($opts{c}); my $quiet = $opts{q}; my $preserve_backups = $opts{p}; + my $resume_only = $opts{r}; # check command line options if($opts{h} || (not $command)) { @@ -1729,65 +1761,89 @@ MAIN: if($action_run) { - # - # create snapshots - # - my $timestamp = sprintf("%04d%02d%02d", @today); - foreach my $config_vol (@{$config->{VOLUME}}) + if($resume_only) { + INFO "Skipping snapshot creation (option \"-r\" present)"; + } + else { - next if($config_vol->{ABORTED}); - my $sroot = $config_vol->{sroot} || die; - foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) + # + # create snapshots + # + my $timestamp = sprintf("%04d%02d%02d", @today); + foreach my $config_vol (@{$config->{VOLUME}}) { - next if($config_subvol->{ABORTED}); - my $svol = $config_subvol->{svol} || die; - my $snapdir = config_key($config_subvol, "snapshot_dir") || ""; - my $snapshot_basename = config_key($config_subvol, "snapshot_name") // die; + next if($config_vol->{ABORTED}); + my $sroot = $config_vol->{sroot} || die; + foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) + { + next if($config_subvol->{ABORTED}); + my $svol = $config_subvol->{svol} || die; + my $snapdir = config_key($config_subvol, "snapshot_dir") || ""; + my $snapshot_basename = config_key($config_subvol, "snapshot_name") // die; - # check if we need to create a snapshot - my $create_snapshot = config_key($config_subvol, "snapshot_create_always"); - foreach my $config_target (@{$config_subvol->{TARGET}}) { - next if($config_target->{ABORTED}); - $create_snapshot = 1 if($config_target->{target_type} eq "send-receive"); - } - unless($create_snapshot) { - $config_subvol->{ABORTED} = "No targets defined for subvolume: $svol->{PRINT}"; - WARN "Skipping subvolume section: $config_subvol->{ABORTED}"; - next; - } - - # find unique snapshot name - my @unconfirmed_target_name; - my @lookup = keys %{vinfo_subvol_list($sroot)}; - @lookup = grep s/^\Q$snapdir\E\/// , @lookup; - foreach my $config_target (@{$config_subvol->{TARGET}}) { - if($config_target->{ABORTED}) { - push(@unconfirmed_target_name, vinfo($config_target->{url}, $config_target)); + # check if we need to create a snapshot + my $snapshot_create = config_key($config_subvol, "snapshot_create") // "no"; + if($snapshot_create eq "no") { + DEBUG "Snapshot creation disabled: snapshot_create=no"; next; } - my $droot = $config_target->{droot} || die; - push(@lookup, keys %{vinfo_subvol_list($droot)}); - } - @lookup = grep /^\Q$snapshot_basename.$timestamp\E(_[0-9]+)?$/ ,@lookup; - TRACE "Present snapshot names for \"$svol->{PRINT}\": " . join(', ', @lookup); - @lookup = map { /_([0-9]+)$/ ? $1 : 0 } @lookup; - @lookup = sort { $b <=> $a } @lookup; - my $postfix_counter = $lookup[0] // -1; - $postfix_counter++; - my $snapshot_name = $snapshot_basename . '.' . $timestamp . ($postfix_counter ? "_$postfix_counter" : ""); + if($snapshot_create eq "always") { + DEBUG "Snapshot creation enabled: snapshot_create=always"; + } + elsif($snapshot_create eq "ondemand") { + my $snapshot_needed = 0; + foreach my $config_target (@{$config_subvol->{TARGET}}) { + next if($config_target->{ABORTED}); + if($config_target->{target_type} eq "send-receive") { + $snapshot_needed = 1; + last; + } + } + if($snapshot_needed) { + DEBUG "Snapshot creation enabled: snapshot_create=ondemand, and at least one send-receive target is present"; + } + else { + DEBUG "Snapshot creation disabled: snapshot_create=ondemand, but no send-receive target is present"; + next; + } + } + else { + die "illegal value for snapshot_create configuration option: $snapshot_create"; + } - if(@unconfirmed_target_name) { - INFO "Failed to check all targets, assuming non-present subvolume \"$snapshot_name\" in: " . join(", ", map { "\"$_->{PRINT}\"" } @unconfirmed_target_name); - } + # find unique snapshot name + my @unconfirmed_target_name; + my @lookup = keys %{vinfo_subvol_list($sroot)}; + @lookup = grep s/^\Q$snapdir\E\/// , @lookup; + foreach my $config_target (@{$config_subvol->{TARGET}}) { + if($config_target->{ABORTED}) { + push(@unconfirmed_target_name, vinfo($config_target->{url}, $config_target)); + next; + } + my $droot = $config_target->{droot} || die; + push(@lookup, keys %{vinfo_subvol_list($droot)}); + } + @lookup = grep /^\Q$snapshot_basename.$timestamp\E(_[0-9]+)?$/ ,@lookup; + TRACE "Present snapshot names for \"$svol->{PRINT}\": " . join(', ', @lookup); + @lookup = map { /_([0-9]+)$/ ? $1 : 0 } @lookup; + @lookup = sort { $b <=> $a } @lookup; + my $postfix_counter = $lookup[0] // -1; + $postfix_counter++; + my $snapshot_name = $snapshot_basename . '.' . $timestamp . ($postfix_counter ? "_$postfix_counter" : ""); - # finally create the snapshot - INFO "Creating subvolume snapshot for: $svol->{PRINT}"; - if(btrfs_subvolume_snapshot($svol, "$sroot->{PATH}/$snapdir/$snapshot_name")) { - $config_subvol->{SNAPSHOT} = vinfo_child($sroot, "$snapdir/$snapshot_name"); - } - else { - $config_subvol->{ABORTED} = "Failed to create snapshot: $svol->{PRINT} -> $sroot->{PRINT}/$snapdir/$snapshot_name"; - WARN "Skipping subvolume section: $config_subvol->{ABORTED}"; + if(@unconfirmed_target_name) { + INFO "Failed to check all targets, assuming non-present subvolume \"$snapshot_name\" in: " . join(", ", map { "\"$_->{PRINT}\"" } @unconfirmed_target_name); + } + + # finally create the snapshot + INFO "Creating subvolume snapshot for: $svol->{PRINT}"; + if(btrfs_subvolume_snapshot($svol, "$sroot->{PATH}/$snapdir/$snapshot_name")) { + $config_subvol->{SNAPSHOT} = vinfo_child($sroot, "$snapdir/$snapshot_name"); + } + else { + $config_subvol->{ABORTED} = "Failed to create snapshot: $svol->{PRINT} -> $sroot->{PRINT}/$snapdir/$snapshot_name"; + WARN "Skipping subvolume section: $config_subvol->{ABORTED}"; + } } } } @@ -1805,6 +1861,7 @@ MAIN: my $svol = $config_subvol->{svol} || die; my $snapdir = config_key($config_subvol, "snapshot_dir") || ""; my $snapshot_basename = config_key($config_subvol, "snapshot_name") // die; + my $preserve_latest = $config_subvol->{SNAPSHOT} ? 0 : 1; foreach my $config_target (@{$config_subvol->{TARGET}}) { @@ -1814,11 +1871,9 @@ MAIN: if($target_type eq "send-receive") { - if(config_key($config_target, "receive_log")) { - WARN "Ignoring deprecated option \"receive_log\" for target: $droot->{PRINT}" - } - + # # resume missing backups (resume_missing) + # if(config_key($config_target, "resume_missing")) { INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/"; @@ -1863,6 +1918,7 @@ MAIN: preserve_daily => config_key($config_target, "target_preserve_daily"), preserve_weekly => config_key($config_target, "target_preserve_weekly"), preserve_monthly => config_key($config_target, "target_preserve_monthly"), + preserve_latest => $preserve_latest, ); my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes) $resume_total = scalar @resume; @@ -1894,20 +1950,23 @@ MAIN: } else { INFO "No missing backups found"; } + } # /resume_missing + + unless($resume_only) + { + # skip creation if resume_missing failed + next if($config_target->{ABORTED}); + next unless($config_subvol->{SNAPSHOT}); + + # finally receive the previously created snapshot + INFO "Creating subvolume backup (send-receive) for: $svol->{PRINT}"; + my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot); + macro_send_receive($config_target, + snapshot => $config_subvol->{SNAPSHOT}, + target => $droot, + parent => $latest_common_src, # this is if no common found + ); } - - # skip creation if resume_missing failed - next if($config_target->{ABORTED}); - die unless($config_subvol->{SNAPSHOT}); - - # finally receive the previously created snapshot - INFO "Creating subvolume backup (send-receive) for: $svol->{PRINT}"; - my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot); - macro_send_receive($config_target, - snapshot => $config_subvol->{SNAPSHOT}, - target => $droot, - parent => $latest_common_src, # this is if no common found - ); } else { ERROR "Unknown target type \"$target_type\", skipping: $svol->{PRINT}"; @@ -1921,8 +1980,8 @@ MAIN: # # remove backups following a preserve daily/weekly/monthly scheme # - if($preserve_backups) { - INFO "Preserving all backups (option \"-p\" present)"; + if($preserve_backups || $resume_only) { + INFO "Preserving all backups (option \"-p\" or \"-r\" present)"; } else { @@ -1936,7 +1995,9 @@ MAIN: my $svol = $config_subvol->{svol} || die; my $snapdir = config_key($config_subvol, "snapshot_dir") || ""; my $snapshot_basename = config_key($config_subvol, "snapshot_name") // die; + my $preserve_latest = $config_subvol->{SNAPSHOT} ? 0 : 1; my $target_aborted = 0; + foreach my $config_target (@{$config_subvol->{TARGET}}) { if($config_target->{ABORTED}) { @@ -1968,6 +2029,7 @@ MAIN: preserve_daily => config_key($config_target, "target_preserve_daily"), preserve_weekly => config_key($config_target, "target_preserve_weekly"), preserve_monthly => config_key($config_target, "target_preserve_monthly"), + preserve_latest => $preserve_latest, log_verbose => 1, ); my $ret = btrfs_subvolume_delete($delete, commit => config_key($config_target, "btrfs_commit_delete")); @@ -2003,6 +2065,7 @@ MAIN: preserve_daily => config_key($config_subvol, "snapshot_preserve_daily"), preserve_weekly => config_key($config_subvol, "snapshot_preserve_weekly"), preserve_monthly => config_key($config_subvol, "snapshot_preserve_monthly"), + preserve_latest => $preserve_latest, log_verbose => 1, ); my $ret = btrfs_subvolume_delete($delete, commit => config_key($config_subvol, "btrfs_commit_delete")); @@ -2033,19 +2096,19 @@ MAIN: my $sroot = $config_vol->{sroot} || vinfo($config_vol->{url}, $config_vol); foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) { + my @subvol_out; my $svol = $config_subvol->{svol} || vinfo_child($sroot, $config_subvol->{rel_path}); - push @out, "$svol->{PRINT}"; if($config_vol->{ABORTED}) { - push @out, "!!! $sroot->{PRINT}: ABORTED: $config_vol->{ABORTED}"; + push @subvol_out, "!!! $sroot->{PRINT}: ABORTED: $config_vol->{ABORTED}"; $err_count++ unless($config_vol->{ABORTED_NOERR}); } if($config_subvol->{ABORTED}) { - push @out, "!!! Subvolume \"$svol->{PRINT}\" aborted: $config_subvol->{ABORTED}"; + push @subvol_out, "!!! Subvolume \"$svol->{PRINT}\" aborted: $config_subvol->{ABORTED}"; $err_count++ unless($config_subvol->{ABORTED_NOERR}); } - push @out, "+++ $config_subvol->{SNAPSHOT}->{PRINT}" if($config_subvol->{SNAPSHOT}); + push @subvol_out, "+++ $config_subvol->{SNAPSHOT}->{PRINT}" if($config_subvol->{SNAPSHOT}); if($config_subvol->{SUBVOL_DELETED}) { - push @out, "--- $_->{PRINT}" foreach(sort { $b->{PATH} cmp $a->{PATH} } @{$config_subvol->{SUBVOL_DELETED}}); + push @subvol_out, "--- $_->{PRINT}" foreach(sort { $a->{PATH} cmp $b->{PATH} } @{$config_subvol->{SUBVOL_DELETED}}); } foreach my $config_target (@{$config_subvol->{TARGET}}) { @@ -2055,21 +2118,26 @@ MAIN: $create_mode = ">>>" if($_->{parent}); # substr($create_mode, 0, 1, '%') if($_->{resume}); $create_mode = "!!!" if($_->{ERROR}); - push @out, "$create_mode $_->{received_subvolume}->{PRINT}"; + push @subvol_out, "$create_mode $_->{received_subvolume}->{PRINT}"; } if($config_target->{SUBVOL_DELETED}) { - push @out, "--- $_->{PRINT}" foreach(sort { $b->{PATH} cmp $a->{PATH} } @{$config_target->{SUBVOL_DELETED}}); + push @subvol_out, "--- $_->{PRINT}" foreach(sort { $a->{PATH} cmp $b->{PATH} } @{$config_target->{SUBVOL_DELETED}}); } if($config_target->{ABORTED}) { - push @out, "!!! Target \"$droot->{PRINT}\" aborted: $config_target->{ABORTED}"; + push @subvol_out, "!!! Target \"$droot->{PRINT}\" aborted: $config_target->{ABORTED}"; $err_count++ unless($config_target->{ABORTED_NOERR}); } push(@unrecoverable, $config_target->{UNRECOVERABLE}) if($config_target->{UNRECOVERABLE}); } - push @out, ""; + if(@subvol_out) { + push @out, "$svol->{PRINT}", @subvol_out, ""; + } + else { + push @out, "$svol->{PRINT}", "", ""; + } } } @@ -2087,8 +2155,11 @@ MAIN: print join("\n", @out); - if($preserve_backups) { - print "\nNOTE: Preserved all backups (option -p present)\n"; + if($resume_only) { + print "\nNOTE: No snapshots created (option -r present)\n"; + } + if($preserve_backups || $resume_only) { + print "\nNOTE: Preserved all backups (option -p or -r present)\n"; } if($err_count) { print "\nNOTE: Some errors occurred, which may result in missing backups!\n"; diff --git a/btrbk.conf.example b/btrbk.conf.example index 5237376..44777a9 100644 --- a/btrbk.conf.example +++ b/btrbk.conf.example @@ -22,14 +22,15 @@ snapshot_dir _btrbk_snap # Perform incremental backups (set to "strict" if you want to prevent -# creation of initial backups if no parent is found). +# creation of non-incremental backups if no parent is found). incremental yes -# Always create snapshots, even if the target volume is unreachable. -snapshot_create_always yes +# Always create snapshots (set to "ondemand" to only create snapshots +# if the target volume is reachable). +snapshot_create always -# Resume missing backups if the target volume is reachable again. -# Useful in conjunction with "snapshot_create_always". +# Resume missing backups if the target volume is reachable again (set +# to "no" if you don't want to resume missing backups). resume_missing yes # ssh key for ssh volumes/targets @@ -115,3 +116,15 @@ volume ssh://my-remote-host.com/mnt/btr_pool snapshot_dir snapshots/btrbk snapshot_name data_main target send-receive /mnt/btr_backup/_btrbk/my-remote-host.com + + +# Resume backups from remote host which runs its own btrbk instance +# creating snapshots for "home" in "/mnt/btr_pool/btrbk_snapshots". +volume ssh://my-remote-host.com/mnt/btr_pool + subvolume home + snapshot_dir btrbk_snapshots + snapshot_preserve_daily all + snapshot_create no + resume_missing yes + + target send-receive /mnt/btr_backup/_btrbk/my-remote-host.com diff --git a/doc/btrbk.conf.5 b/doc/btrbk.conf.5 index 6e8faf5..e9358d2 100644 --- a/doc/btrbk.conf.5 +++ b/doc/btrbk.conf.5 @@ -56,14 +56,15 @@ Base name of the created snapshot (and backup). Defaults to \fI\fR. This option is only valid in the \fItarget\fR section. .TP -\fBsnapshot_create_always\fR yes|no -If set, the snapshots are always created, even if the backup subvolume -cannot be created (e.g. if the target subvolume cannot be -reached). Use in conjunction with the \fIresume_missing\fR option to -make sure that the backups are created as soon as the target subvolume -is reachable again. Useful for laptop filesystems in order to make -sure the snapshots are created even if you are on the road. Defaults -to \[lq]no\[rq]. +\fBsnapshot_create\fR always|ondemand|no +If set to \[lq]ondemand\[rq], the snapshots are only created if the +target subvolume is reachable (useful if you are tight on disk space +and you only need btrbk for backups to an external disk which is not +always connected). If set to \[lq]always\[rq], the snapshots are +always created, regardless of the target reachability. If set to +\[lq]no\[rq], the snapshots are never created (useful in conjunction +with the \fIresume_missing\fR option if another instance of btrbk is +taking care of snapshot creation). Defaults to \[lq]always\[rq]. .TP \fBincremental\fR yes|no|strict Perform incremental backups. Defaults to \[lq]yes\[rq]. If set to