Let’s make our first backup. We have to create our keypair for encryption/decrytion first:
$ uback key gen backup.key backup.pub
backup.key
is the private key, used only for restoring your backups. You
should keep it in a safe (losing it means losing all your backups), secure
(disclosing it means anyone that know it can decrypt your backups) place,
preferably offline. backup.pub
is the public key, used for creating
the backups. You can recreate it from the private key if you lose it ;
it cannot be used for decrypting your backups, so it is not important
to keep it secret.
They are standard age keys ; you can use
age-keygen
to generate/manage them.
Then, let’s create stuff we want to backup :
$ mkdir /tmp/etc
$ echo hello > /tmp/etc/a
We can now create our encrypted backup :
$ uback backup type=tar,path=/tmp/etc,key-file=backup.pub type=fs,path=/tmp/my-backups/etc/
WARN[0000] SnapshotsPath option missing, incremental backups will not impossible source=tar
WARN[0000] StateFile option missing, full backup forced
INFO[0000] creating backup: 20210515T124130.706-full.ubkp source=tar
INFO[0000] running: /usr/bin/tar --create -C /tmp/etc .
INFO[0000] writing backup to /tmp/my-backups/etc/_tmp-20210515T124130.706-full.ubkp destination=fs
INFO[0000] moving final backup to /tmp/my-backups/etc/20210515T124130.706-full.ubkp destination=fs
INFO[0000] deleting snapshot snapshot=20210515T124130.706
WARN[0000] no retention policies set for destination, keeping everything
The three warnings will be addressed in the next sections.
Most options here are pretty much self-explanatory. But to state the
obvious, path=/tmp/etc
specifies what we’re backing up, type=tar
how we
bundle it, key-file
is the public key use for encrypting the backup,
path=/tmp/my-backups/etc
is the location of the backups, and type=fs
means that we’re just storing the backups as plain files on the local
filesystem. The id
of the destination is used for bookkeeping purposes ;
it must be unique for each source-destination pair (in other words,
if you backup a source to multiple destinations, each destination must
have a different ID).
Note that backups are directly saved to the destination path
(you
can check by running ls /tmp/my-backups/etc
) ; there is nothing to
identify the source except the etc
name we manually put in the path
configuration of the destination. If you want to save different sources
(for example /etc
and /var/lib
), you should put them in different
paths in the destination too, otherwise backups of different sources will
be intermingled. This a general feature of destinations ; for example,
if you want to backup different souces on a same S3-compatible object
storage, you should put them in different buckets or give them different
prefixes if they are in the same bucket.
We can see that the backup has been created :
$ uback list backups type=fs,path=/tmp/my-backups/etc/
20210515T124130.706 (full)
We can try to restore it :
$ uback restore -d /tmp/restored-etc/ type=fs,path=/tmp/my-backups/etc/,key-file=backup.key
INFO[0000] restoring 20210515T124130.706-full.ubkp onto /tmp/restored-etc/
INFO[0000] running /usr/bin/tar -x -C /tmp/restored-etc/20210515T124130.706 source=tar
And we can see that the backup has been restored :
$ cat /tmp/restored-etc/20210515T124130.706/a
hello
Let’s go back to the two warnings we got in the previous section :
WARN[0000] SnapshotsPath option missing, incremental backups will not impossible source=tar
WARN[0000] StateFile option missing, full backup forced
tar
supports incremental archives (if you’re curious, see the
--listed-incremental
option in the tar(1)
manual). uback
can make
use of this feature to create incremental backups.
To support incremental backups in uback
, we need to provide two options
to the source :
-
The snapshots directory, where
uback
stores the information required to create a new incremental backup from a previous made one. This option is specific to the source type (here,tar
) : different sources types may have different way to store this information. For thetar
source type, this is given by thesnapshots-path
option. -
The state file, where
uback
keeps tracks of the last snapshot on each destination, allowing us to remove unused ones. This is not specific to the source type and is needed as soon as you want incremental backups. It is given by thestate-file
option.
First, let’s create a (relatively) big file (so we can see the effect of incremental backups), a full backup and its associated snapshot :
$ dd if=/dev/urandom bs=1M count=10 of=/tmp/etc/BIG
$ uback backup type=tar,path=/tmp/etc,key-file=backup.pub,state-file=/tmp/uback/state/etc.json,snapshots-path=/tmp/uback/tar-snapshots/etc/,full-interval=weekly id=fs,type=fs,path=/tmp/my-backups/etc/
WARN[0000] no common snapshots found, full backup forced
INFO[0000] creating backup: 20210515T130148.562-full.ubkp source=tar
INFO[0000] running: /usr/bin/tar --create --listed-incremental=/tmp/uback/tar-snapshots/etc/_tmp-20210515T130148.562 -C /tmp/etc .
INFO[0000] writing backup to /tmp/my-backups/etc/_tmp-20210515T130148.562-full.ubkp destination=fs
INFO[0000] moving final backup to /tmp/my-backups/etc/20210515T130148.562-full.ubkp destination=fs
WARN[0000] no retention policies set for destination, keeping everything
(note that because we didn’t specify a snapshots-path
in the previous
section, we can’t reuse the full backup created then and have to create
a brand new one.)
Then let’s change the content of a
and create an incremental backup :
$ echo hello world > /tmp/etc/a
$ uback backup type=tar,path=/tmp/etc,key-file=backup.pub,state-file=/tmp/uback/state/etc.json,snapshots-path=/tmp/uback/tar-snapshots/etc/,full-interval=weekly id=fs,type=fs,path=/tmp/my-backups/etc/
INFO[0000] creating backup: 20210515T130258.315-from-20210515T130148.562.ubkp source=tar
INFO[0000] running: /usr/bin/tar --create --listed-incremental=/tmp/uback/tar-snapshots/etc/_tmp-20210515T130258.315 -C /tmp/etc .
INFO[0000] writing backup to /tmp/my-backups/etc/_tmp-20210515T130258.315-from-20210515T130148.562.ubkp destination=fs
INFO[0000] moving final backup to /tmp/my-backups/etc/20210515T130258.315-from-20210515T130148.562.ubkp destination=fs
INFO[0000] deleting snapshot snapshot=20210515T130148.562
WARN[0000] no retention policies set for destination, keeping everything
Notice the name of the backup,
20210515T130258.315-from-20210515T130148.562.ubkp
, which indicates
that an incremental backup has indeed been created. We can look at the
size of the incremental backup, to check that it does not contains the
BIG
file which has not been modified between the two backups :
$ ls -lh /tmp/my-backups/etc/
total 11M
-rw-r--r-- 1 user user 260 May 15 14:41 20210515T124130.706-full.ubkp
-rw-r--r-- 1 user user 11M May 15 15:01 20210515T130148.562-full.ubkp
-rw-r--r-- 1 user user 231 May 15 15:02 20210515T130258.315-from-20210515T130148.562.ubkp
Note the full-interval
option : it controls the time between two full
backups. More precisely, a full backup will be created instead of an
incremental one if the age of the most recent full backup is bigger than
the time specified in full-interval
. full-interval
can be :
10
for 10 seconds,10h
for 10 hours,10d
for 10 days (10*24 hours),10w
for 10 weeks (10*7 days),10m
for 10*30 days,10y
for 10*365 days,hourly
is an alias for1h
daily
is an alias for1d
weekly
is an alias for1w
monthly
is an alias for1m
yearly
is an alias for1y
Finally, let’s restore the last backup to check that the BIG
file will
be properly restored even if not present in the last incremental backup :
$ sha256sum /tmp/etc/BIG
903803469a5c720b22c388853c92abba1b69c5f351a625ffdab41e06b493cd7a /tmp/etc/BIG
$ uback restore -d /tmp/restored-etc/ type=fs,path=/tmp/my-backups/etc/,key-file=backup.key
INFO[0000] restoring 20210515T130148.562-full.ubkp onto /tmp/restored-etc/
INFO[0000] running /usr/bin/tar -x -C /tmp/restored-etc/20210515T130148.562 source=tar
INFO[0000] restoring 20210515T130258.315-from-20210515T130148.562.ubkp onto /tmp/restored-etc/
INFO[0000] running /usr/bin/tar -x -C /tmp/restored-etc/20210515T130258.315 source=tar
$ sha256sum /tmp/restored-etc/20210515T130258.315/BIG
903803469a5c720b22c388853c92abba1b69c5f351a625ffdab41e06b493cd7a /tmp/restored-etc/20210515T130258.315/BIG
The command lines in the previous were quite mouthful. And we haven’t
introduced retention policies yet, which will make even more bigger ! To
keep them under control, uback
has a system of presets, that allow
you to define a set of options and re-use them.
First, let’s make a big simple preset that we can use for our source :
$ uback preset set etc-src type=tar,path=/tmp/etc,key-file=backup.pub,state-file=/tmp/uback/state/etc.json,snapshots-path=/tmp/uback/tar-snapshots/etc/,full-interval=weekly
$ uback preset list -v
etc-src [[Type tar] [Path /tmp/etc] [KeyFile backup.pub] [StateFile /tmp/uback/state/etc.json] [SnapshotsPath /tmp/uback/tar-snapshots/etc/] [FullInterval weekly]]
$ uback backup preset=etc-src id=fs,type=fs,path=/tmp/my-backups/etc/
INFO[0000] creating backup: 20210515T131759.943-from-20210515T130258.315.ubkp source=tar
INFO[0000] running: /usr/bin/tar --create --listed-incremental=/tmp/uback/tar-snapshots/etc/_tmp-20210515T131759.943 -C /tmp/etc .
INFO[0000] writing backup to /tmp/my-backups/etc/_tmp-20210515T131759.943-from-20210515T130258.315.ubkp destination=fs
INFO[0000] moving final backup to /tmp/my-backups/etc/20210515T131759.943-from-20210515T130258.315.ubkp destination=fs
INFO[0000] deleting snapshot snapshot=20210515T130258.315
WARN[0000] no retention policies set for destination, keeping everything
A preset is a list of key-value pairs that store options. We can see that
internally, options in uback
are formatted in PascalCase
. The case
conversion is made automatically when you specify options on the command
line ; however, if you want to write your own sources/destinations or
want to manipulate the preset files directly, it is something you want
to keep in mind.
Presets are stored as simple JSON files that you can manipulate manually or from your own scripts :
$ cat $HOME/.config/uback/presets/etc-src.json
[["Type","tar"],["Path","/tmp/etc"],["KeyFile","backup.pub"],["StateFile","/tmp/uback/state/etc.json"],["SnapshotsPath","/tmp/uback/tar-snapshots/etc/"],["FullInterval","weekly"]]
Presets can reference other presets, so we can mare a generic src
preset that will define the KeyFile
and FullInterval
options, a
tar-src
preset that will reference it, and build our etc-src
from it :
$ uback preset remove etc-src
$ uback preset set src key-file=backup.pub,full-interval=weekly
$ uback preset set tar-src type=tar,preset=src
$ uback preset set etc-src preset=tar-src,path=/tmp/etc,state-file=/tmp/uback/state/etc.json,snapshots-path=/tmp/uback/tar-snapshots/etc/
$ uback preset list -v
etc-src [[Preset tar-src] [Path /tmp/etc] [StateFile /tmp/uback/state/etc.json] [SnapshotsPath /tmp/uback/tar-snapshots/etc/]]
src [[KeyFile backup.pub] [FullInterval weekly]]
tar-src [[Type tar] [Preset src]]
$ uback preset eval preset=etc-src
KeyFile: backup.pub
FullInterval: weekly
Path: /tmp/etc
StateFile: /tmp/uback/state/etc.json
SnapshotsPath: /tmp/uback/tar-snapshots/etc/
Type: tar
(Note that we have specified backup.pub
as a relative file. In a real
setup, you should really use the full absolute path for it.)
It’s nice, but it would be even nicer to be somewhat able to put
the StateFile
option in the src
preset and the SnapshotsPath
in the tar-src
preset, but in a modular way so other sources can
make use of those presets. The good news is: since values are Go
templates, it is indeed possible :
$ uback preset remove etc-src src tar-src
$ uback preset set src key-file=backup.pub,full-interval=weekly,state-file=/tmp/uback/state/{{.Path}}.json
$ uback preset set tar-src type=tar,preset=src,snapshots-path=/tmp/uback/tar-snapshots/{{.Path}}
$ uback preset set etc-src preset=tar-src,path=/tmp/etc
$ uback preset list -v
src [[KeyFile backup.pub] [FullInterval weekly] [StateFile /tmp/uback/state/{{.Path}}.json]]
tar-src [[Type tar] [Preset src] [SnapshotsPath /tmp/uback/tar-snapshots/{{.Path}}]]
etc-src [[Preset tar-src] [Path /tmp/etc]]
$ uback preset eval preset=etc-src
Path: /tmp/etc
Type: tar
KeyFile: backup.pub
FullInterval: weekly
StateFile: /tmp/uback/state/<no value>.json
SnapshotsPath: /tmp/uback/tar-snapshots/<no value>
Oops ! something went wrong here. What happened ? tar-src
and src
presets made use of the Path
option, but were included before the
option was set. Let’s fix that :
$ uback preset remove etc-src
$ uback preset set etc-src path=/tmp/etc,preset=tar-src
$ uback preset eval preset=etc-src
Path: /tmp/etc
Type: tar
KeyFile: backup.pub
FullInterval: weekly
StateFile: /tmp/uback/state//tmp/etc.json
SnapshotsPath: /tmp/uback/tar-snapshots//tmp/etc
Far better ! But there is still one issue here, which is that the path is
inserted verbatim in the StateFile
and SnapshotsPath
options. What if
we don't want a deep hierarchy in /tmp/uback/tar-snapshots/
? What if
we set path
to /tmp/etc/
? Then the StateFile
option will be set to
/tmp/uback/state//tmp/etc/.json
, which can be surprising.
Thankfully, there is a solution. Values are Go templates that includes Sprig Functions.
First, let’s change src
: it no longer uses the Path
option,
but must be provided an SourceID
option :
$ uback preset remove src
$ uback preset set src key-file=backup.pub,full-interval=weekly,state-file=/tmp/uback/state/{{.SourceID}}.json
Then, let’s make tar-src
make that SourceID
option from the Path
option, and use it itself for its SnapshotsPath
option :
$ uback preset remove tar-src
$ uback preset set tar-src 'type=tar,source-id={{.Path|clean|replace "/" "-"|trimSuffix "-"}},preset=src,snapshots-path=/tmp/uback/tar-snapshots/{{.SourceID}}'
$ uback preset eval preset=etc-src
SourceID: -tmp-etc
KeyFile: backup.pub
FullInterval: weekly
StateFile: /tmp/uback/state/-tmp-etc.json
SnapshotsPath: /tmp/uback/tar-snapshots/-tmp-etc
Path: /tmp/etc
Type: tar
Finally, let’s just double-check that we can reuse those preset for other sources :
$ uback preset eval path=/tmp/var/lib,preset=tar-src
SourceID: -tmp-var-lib
KeyFile: backup.pub
FullInterval: weekly
StateFile: /tmp/uback/state/-tmp-var-lib.json
SnapshotsPath: /tmp/uback/tar-snapshots/-tmp-var-lib
Path: /tmp/var/lib
Type: tar
Let’s now tackle the last remaining warning :
WARN[0000] no retention policies set for destination, keeping everything
Retention policies specifies what backups (and snapshots) you want to
retain (keep). Pruning means removing every backup/snapshot that is not
retained by the retention policy. Pruning is automatically performed
after every backup (except if the --no-prune
flag is specified on the
command line) and can be manually performed with uback prune
.
For backups, the default policy is to retain everything.
For snapshots, the default policy is to retain nothing except the
snapshots that are present in StateFile
, which just means the latest
successful backup present on each destination.
Snapshots retention policies are specified on the source, whereas backups retention policies are specified on the destination.
We will not discuss snapshots retention and pruning ; it works exactly
the same way as backups, but most of the time snapshots are not useful to
keep around and the default policy of only keeping the last one (for the
ability to create an incremental backup from it) is perfectly fine. In
some rare situations (for example with btrfs
source) a snapshot can
be useful by itself and you may want to specify a retention policy for
the snapshots to keep them around.
A retention policy consists of three pieces of information :
- An interval
- A count
- An optional
full
option
The syntax is interval=count[:full]
, for example weekly=13:full
or
daily=7
. The syntax for the interval is the same than the syntax for
full-interval
, described earlier in the Incremental Backups
section.
The count
most recents backups are retained, as long as they are
separated by (at least) interval
. If the full
option is specified,
only full backups are considered. Then, all backups required to restore
a retained backups are marked as retained, bottoming up to a full
backup. Eventually, all backups not marked as retained are pruned.
A notable exception to this process is orphan incremental backups, i.e. incremental backups that do not eventually bottom up to an existing full backup. Those are defective (because they cannot be restored) and are always pruned.
For example, let’s say we have those backups :
2020-12-31 (full)
2021-01-01 (full)
2021-01-02 (incremental, from 2021-01-01)
2021-01-03 (incremental, from 2021-01-02)
2021-01-04 (incremental, from 2021-01-03)
2021-01-05 (incremental, from 2021-01-04)
2021-01-06 (incremental, from 2021-01-05)
2021-01-07 (full)
2021-01-08 (incremental, from 2021-01-07)
2021-01-09 (incremental, from 2019-12-01)
2021-01-10 (incremental, from 2021-01-09)
First, let’s note that no matter the retention policy (except the default retention policy which keeps all backups, no exception) the last two backups in this list will always be pruned, because they are orphan incremental backups. Let’s rid them out of the picture and consider the list without orphan backups :
2020-12-31 (full)
2021-01-01 (full)
2021-01-02 (incremental, from 2021-01-01)
2021-01-03 (incremental, from 2021-01-02)
2021-01-04 (incremental, from 2021-01-03)
2021-01-05 (incremental, from 2021-01-04)
2021-01-06 (incremental, from 2021-01-05)
2021-01-07 (full)
2021-01-08 (incremental, from 2021-01-07)
Let’s go through some examples :
-
The
daily=1
retention policy will keep the last two backups : it will mark the last backup as retained, and then keep the one before the last as a dependency. -
The
daily=1:full
retention policy will only keep the2021-01-07
backup. -
The
daily=3:full
retention policy will keep the three full backups, namely2021-01-07
,2021-01-01
and2020-12-31
. -
The
daily=3
retention policy will keep everything except the first backup. -
The
5d=2
retention policy will keep2021-01-08
and2021-01-03
and their dependencies (2021-01-07
,2021-01-02
and2021-01-01
). -
If multiple retention policies are given, all retention policies are evaluated independently, and the backups that are retained are the one that are retained by at least policy (in other words, only the backups that are retained by no retention policy are pruned).
Let’s illustrate all that by creating a fake repository of backups mirroring our list above, and by appling the retention policies 3. and 5. :
$ mkdir -p /tmp/my-backups/test-retention
$ touch /tmp/my-backups/test-retention/20201231T000000.000-full.ubkp
$ touch /tmp/my-backups/test-retention/20210101T000000.000-full.ubkp
$ touch /tmp/my-backups/test-retention/20210102T000000.000-from-20210101T000000.000.ubkp
$ touch /tmp/my-backups/test-retention/20210103T000000.000-from-20210102T000000.000.ubkp
$ touch /tmp/my-backups/test-retention/20210104T000000.000-from-20210103T000000.000.ubkp
$ touch /tmp/my-backups/test-retention/20210105T000000.000-from-20210104T000000.000.ubkp
$ touch /tmp/my-backups/test-retention/20210106T000000.000-from-20210105T000000.000.ubkp
$ touch /tmp/my-backups/test-retention/20210107T000000.000-full.ubkp
$ touch /tmp/my-backups/test-retention/20210108T000000.000-from-20210107T000000.000.ubkp
$ uback prune backups -n type=fs,path=/tmp/my-backups/test-retention,@retention-policy=daily=3:full,@retention-policy=5d=2
20210106T000000.000
20210105T000000.000
20210104T000000.000
$ uback prune backups type=fs,path=/tmp/my-backups/test-retention,@retention-policy=daily=3:full,@retention-policy=5d=2
20210106T000000.000
20210105T000000.000
20210104T000000.000
$ uback list backups type=fs,path=/tmp/my-backups/test-retention,@retention-policy=daily=3:full,@retention-policy=5d=2
20201231T000000.000 (full)
20210101T000000.000 (full)
20210102T000000.000 (base: 20210101T000000.000)
20210103T000000.000 (base: 20210102T000000.000)
20210107T000000.000 (full)
20210108T000000.000 (base: 20210107T000000.000)
Two things to note here :
-
The retention policies are specified as a destination option for backups. The name of the option is
@retention-policy
; the@
indicates that the option can have multiple values (here, a destination can have multiple retention policies). The@
symbol must be specified even if you have only one retention policy. -
prune -n
allows you to see what a retention policy would prune without actually removing the backups, for testing purposes. Once you are sastified with a retention policy, you can add it to your preset or your backup command for automatic pruning on every backup.