Duplicity is a space- and bandwidth-efficient backup software utility built atop the rsync and GnuPG technologies. It provides a relatively simple way to create full or incremental backups, either from a command line or using a variety of graphical user interfaces. By default, it uses password-based (symmetric) encryption to protect data at rest, and is compatible with numerous storage backends including Free Software and even technocapitalist SAASS offerings.
We recommend the use of duplicity(1)
as a simple and effective means of being prepared for disaster recovery.
GNU/Linux users may already have duplicity
installed on their systems. If you do not, on Debian-derived GNU/Linux distributions, install duplicity
with:
sudo apt install duplicity
This section describes several basic uses of duplicity
.
In this configuration, cron
is used to schedule an unattended backup of a given important directory, which will be encrypted locally before being stored on the destination server. The remote host is accessed as a Tor Onion service exposing an rsync
daemon. When the configuration is complete, a single cron job of the following form will automatically back up the important directory:
PASSPHRASE="$(/bin/cat $HOME/.duplicity.secret)" RSYNC_CONNECT_PROG='/bin/nc -x 127.0.0.1:9050 \%H 873' /usr/bin/duplicity --gpg-options="--cipher-algo=AES256" --rsync-options="--password-file=$HOME/.rsync.secret --partial --compress" /path/to/important/directory rsync://rsync_user@abcdef0123456789.onion::backups/ >/dev/null 2>&1
Defining a backup schedule is the responsibility of the data owner, as is determining how many backups to keep and for how long. In this configuration, the following backup policy is assumed, but you should change this to suit your needs by editing the relevant cron job schedules and duplicity
command options:
Assumptions:
$HOME/.duplicity.secret
be a file containing your backup archive password. Make sure this file is chmod 600
.backup_user
be the user that will be running the backups on the local machine.$HOME/.rsync.secret
be your rsync user account’s login password. Make sure this file is chmod 660
.rsync_user
be the rsync account name that the backup_user
will use to login to the rsync server.abcdef0123456789.onion
be your remote rsync Onion service. It is assumed that you have already configured your Tor client to authenticate succesfully, and that an rsync daemon is listening./path/to/important/directory
be the filesystem path containing the data you want to backup.backups/
be the exported rsync module that you are backing up to.Do this to set up automated symmetrically encrypted backups using duplicity
that are stored on a (hopefully off-site) rsync
Tor Onion service.
backup_user
.ssh the.server.local
sudo su --shell /bin/bash --login backup_user
.duplicity.secret
). This will be used to ensure the remote host never receives a plaintext copy of any filesystem data. Be certain to record this password somewhere safe (such as a password/secret manager application), as you will need it to restore the (encrypted!) backup archive.# Collect 45 random bytes (from `/dev/random`?) and base64 encode them.
gpg --armor --gen-random 2 45 > .duplicity.secret
# Ensure the password file is not readable by any other users.
chmod 600 .duplicity.secret
vim .rsync.secret # Put the password to your rsync user account in this file.
# Ensure the password file is not readable by any other users.
chmod 600 .rsync.secret
crontab(5)
file for the user that will be running the backups:crontab -e
%
) must be escaped, as they have special meaning to most implementations of the cron
daemon.# For more information see the manual pages of crontab(5) and cron(8)
# Cron entry fields:
#
# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │ 7 is also Sunday on some systems)
# │ │ │ │ │
# * * * * * command to execute
# Run (incremental) backups to a remote Onion at 3:05am every Tuesday.
5 3 * * 2 PASSPHRASE="$(/bin/cat $HOME/.duplicity.secret)" RSYNC_CONNECT_PROG='/bin/nc -x 127.0.0.1:9050 \%H 873' /usr/bin/duplicity --full-if-older-than 2M --gpg-options="--cipher-algo=AES256" --rsync-options="--password-file=$HOME/.rsync.secret --partial --compress" /path/to/important/directory rsync://rsync_user@abcdef0123456789.onion::backups/ >/dev/null 2>&1
# Remove the 13th (and older) full backups, i.e., retain the most recent 12 full backups and their incremental diffs.
5 4 * * 2 RSYNC_CONNECT_PROG='/bin/nc -x 127.0.0.1:9050 \%H 873' /usr/bin/duplicity remove-all-but-n-full 12 --force --rsync-options="--password-file=$HOME/.rsync.secret --partial --compress" rsync://rsync_user@abcdef0123456789.onion::backups/ >/dev/null 2>&1
A breakdown of the command invocations:
PASSPHRASE="$(/bin/cat/ $HOME/.duplicity.secret)"
– Save the contents of the file $HOME/.duplicity.secret
as the environment variable PASSPHRASE
. This variable is checked by duplicity(1)
, which will pass it to gpg
to use as the encryption password. This is only required when encrypting (backing up).RSYNC_CONNECT_PROG='/bin/nc -x 127.0.0.1:9050 \%H 873'
– Set the environment variable RSYNC_CONNECT_PROG
to a netcat (nc
) proxy (-x
) command. The sequence %H
is replaced by rsync
itself with the address of the remote host. It is backslash-escaped because %
characters are treated specially in crontab(5)
files./usr/bin/duplicity
invoke duplicity(1)
, with the following options and arguments:
--full-if-older-than 2M
– If the last full backup is older than 2 months, do a full backup instead of an incremental one.--gpg-options"--cipher-algo=AES256"
– Instruct the gpg
executable to use the AES256 cipher algorithm. This arguably improves the security of the backup archive by using a more modern cipher (AES256
) than the gpg
default (CAST5
).--rsync-options="--password-file="$HOME/.rsync.secret" --partial --compress"
– Instruct rsync
, when invoked, to read its user account’s password from the file $HOME/.rsync.secret
, to record partial transfers, and to compress the datastream before being sent to the network. The --password-file
is needed to allow for non-interactive execution. Meanwhile, partial transfers ensure that interruptions caused by, e.g., network issues, can be resumed on the next run, and compression speeds the transfer by minimizing the amount of data that needs to be emitted on the network link./path/to/important/directory
– The duplicity backup source directory.rsync://rsync_user
@abcdef0123456789.onion::backups/
– The duplicity target URL, i.e., the location where the backup archive will be sent./usr/bin/duplicity
again, but with these alternate instructions:
remove-all-but-n-full 12
– Remove all old backups except the most recent 12
full backups.--force
– Actually do the removal; this is required by duplicity
in order to ensure you mean what you say.>/dev/null 2>&1
– Redirect the command’s STDOUT
to /dev/null
, and redirect the command’s STDERR
to STDOUT
(which is going to /dev/null
).If you need to use sudo(1)
to run the backup command as another user, say, other_user
, you can pass the RSYNC_CONNECT_PROG
environment variable through the sudo
invocation like so:
sudo RSYNC_CONNECT_PROG='nc -x 127.0.0.1:9050 %H 873' -u other_user duplicity --rsync-options="--password-file=/path/to/password/file" rsync://rsync_user@abcdef0123456789.onion::module_name/ /path/to/restoration/directory
To restore a Duplicity backup archive, re-run the same command as that used to create the archive but swap the positions of the source URL and the target directory. For example:
duplicity --rsync-options="--password-file=/path/to/password/file" /path/to/important/directory rsync://rsync_user@backup.server.com::module_name/
duplicity --rsync-options="--password-file=/path/to/password/file" rsync://rsync_user@backup.server.com::module_name/ /path/to/important/directory
Duplicity makes use of a locally installed gpg
to perform encryption and decryption on its behalf. By default, most versions of gpg
use the CAST5
cipher, despite more modern ciphers being available. View the available cipher list by invoking gpg --version
. A good cipher is AES256
.
Then instruct duplicity(1)
to use this cipher when calling gpg
by passing --gpg-options="--cipher-algo=AES256"
.
The AnarchoTech NYC collective provides an Ansible role for automating the setup of Duplicity backup jobs that run on a Raspberry Pi. It can be installed in your local $ANSIBLE_ROLES_PATH
(see Ansible Configuration Settings) for use with an Ansible project with:
ansible-galaxy install https://github.com/AnarchoTechNYC/ansible-role-duplicity/archive/master.tar.gz
duplicity
, including an example backup script.