Backup & Restore
The backup and restore is a procedure for disaster-recovery scenario: it can be used to save the data of an installed module and restore it to a different node or cluster.
Design
The backup engine is Restic which runs inside a container along with Rclone used to inspect the backup repository contents.
Backups are saved inside backup destinations, remote spaces where data are stored. A backup destination can contain multiple backup instances, each module instance has its own sub-directory to avoid conflicts. This sub-directory is the root of the module instance Restic repository.
The system implements the common logic for backup inside the agent with module-backup
command.
Each module can implement module-dump-state
and module-cleanup-state
to prepare/cleanup the data that has to be included in the backup.
The state/environment
file is always included inside the backup, as it is required by the cluster/restore-module
action.
The restore is implemented using a restore-module
action inside the module agent, each module can extend it to implement specific restore steps.
The basic 10restore
step actually runs the Restic restore procedure in a temporary container.
All backups are scheduled by systemd timers. Given a backup with id 1
, it is possible to retrieve the time status with:
- rootless containers, eg.
dokuwiki1
, executed bydokuwiki1
user:systemctl --user status backup1.timer
- rootfull containers, eg.
dnsmasq1
, executed byroot
user:systemctl status backup1-dnsmasq1.timer
Include and exclude files
Whenever possible, containers should use volumes to avoid UID/GID namespace mappings and SELinux issues during backup an restore.
Includes can be added to the state-include.conf
file saved inside AGENT_INSTALL_DIR/etc/
.
In the source tree, the file should be placed under <module>/imageroot/etc/state-include.conf
.
On installed modules, the file will appear on different paths:
- rootless containers, eg.
dokuwiki1
, full path will be/home/dokuwiki1/.config/etc/state-include.conf
- rootfull containers, eg.
dnsmasq1
, full path will be/var/lib/nethserver/dnsmasq1/etc/state-include.conf
Lines are interpreted as path patterns. Only patterns referring to
volumes and the agent state/
directory are considered.
Lines starting with state/
refer to AGENT_STATE_DIR
contents. Eg. to
include mykey.dump
under the AGENT_STATE_DIR
add
state/mykey.dump
Lines starting with volumes/
will be mapped to a volume name. Eg. to
include the whole dokuwiki-data
volume add
volumes/dokuwiki-data
Internally, volumes will be mapped as:
-
<volume_name>
(1-1) for rootless containers; eg. for moduledokuwiki1
, line prefixvolumes/dokuwiki-data
maps to volume namedokuwiki-data
-
<module_id>-<volume_name>
for rootfull containers; eg. for modulednsmasq1
, line prefixvolumes/ data
maps to volume namednsmasq1-data
Volumes listed in state-include.conf
are automatically mounted (and
created if necessary) by the basic 10restore
step of the
restore-module
action.
Excludes can be added to state-exclude.conf
file saved inside the AGENT_INSTALL_DIR
.
For a complete explanation of the patterns, like wildcard characters, see the official Restic documentation to include and exclude files. Note that include and exclude patterns have a slight different syntax.
Save and restore Redis keys
Dump key and state
To save a Redis key, you should:
- dump the key inside the
module-dump-state
command - include the dump inside the backup
Given a module named mymodule
, create the file mymodule/imageroot/bin/module-dump-state
inside the module source tree:
#!/bin/bash
redis-dump module/mymodule1/mykey > mykey.dump
Make sure also module-dump-state
is executable:
chmod a+x mymodule/imageroot/bin/module-dump-state
Then, add the key dump path to mymodule/imageroot/etc/state-include.conf
:
state/mykey.dump
Cleanup state
As best practice, the dump should be removed when the backup has completed.
Given a module named mymodule
, create the file mymodule/imageroot/bin/module-cleanup-state
inside the module source tree:
#!/bin/bash
rm -f mykey.dump
Make sure also module-cleanup-state
is executable:
chmod a+x mymodule/imageroot/bin/module-cleanup-state
Restore key
To restore a Redis key, you should add a step inside the restore-module
action, after index 10.
Given a module named mymodule
, create a file named mymodule/imageroot/actions/restore-module/20loadkey
inside the module source tree:
#!/bin/bash
redis-restore mymodule1/mykey < mykey.dump
Execute a backup
Before executing a backup, you must create a backup repository. The flow to execute a backup will be something like:
- create a backup repository and retrieve its UUID
- retrieve the UUID of the repository
- configure a backup using the repository as target
- retrieve the backup ID
- execute the backup
- Create the repository:
api-cli run add-backup-repository --data '{"name":"BackBlaze repo1","url":"b2:backupns8","parameters":{"b2_account_id":"xxxxxxxxxxxxxxxxxxxxxxxxx","b2_account_key":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"},"provider":"backblaze","password":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}'
- The output will be something like, please note the
id
field:{"password": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "id": "48ce000a-79b7-5fe6-8558-177fd70c27b4"}
- Create a new daily backup named
mybackup
with a retention of 3 snapshots (3 days) which includesdokuwiki1
anddnsmasq1
instances:api-cli run add-backup --data '{"repository":"48ce000a-79b7-5fe6-8558-177fd70c27b4","schedule":"daily","retention":3,"instances":["dokuwiki1","dnsmasq1"],"enabled":true, "name":"mybackup"}'
- The output will the id of the backup:
1
- Run the backup with id
1
:api-cli run run-backup --data '{"id":1}'
For debugging purposes, you can also launch systemd units:
- rootless container, eg.
dokuwiki1
:runagent -m dokuwiki1 systemctl --user start backup1.service
- rootfull container, eg.
dnsmasq1
: systemctl start backup1-dnsmasq1.service
To remove the backup use:
api-cli run remove-backup --data '{"id":1}'
To remove the backup repository:
api-cli run remove-backup-repository --data '{"id":"c7a9cfea-303c-5104-8ab7-39ac9f9842bd"}'
Execute a restore
Before executing a restore, the backup repository should already be configured.
Restore the dokuwiki1
instance at node 1
from repository 48ce000a-79b7-5fe6-8558-177fd70c27b4
:
api-cli run cluster/restore-module --data '{"node":1, "repository":"48ce000a-79b7-5fe6-8558-177fd70c27b4", "path":"dokuwiki/dokuwiki1@3792c7db-9450-4bd3-84a3-034cd0087839","snapshot":""}'
Cluster configuration backup
The cluster/download-cluster-backup
API returns a random URL path where an encrypted
archive is available for download.
curl -O http://127.0.0.1:9311/backup/$(api-cli run cluster/download-cluster-backup | jq -r .path)
If the previous command is successful a file dump.json.gz.gpg
is created
in the current directory.
The file with .gpg
extension is encrypted with the password saved inside /var/lib/nethserver/cluster/state/backup/passphrase
.
To decrypt it run a command like this:
echo <passphrase> | \
gpg --batch -d --passphrase-file /dev/stdin --pinentry-mode loopback -o dump.json.gz dump.json.gz.gpg
The restore procedure can be started from the UI of a new NS8 installation: upload the file and specify the password from the UI.
The restic-wrapper
command
The Restic binary is not installed in the host system. NS8 runs Restic within a core container, preparing environment variables with values read from the Redis DB and properly mounting the application Podman volumes.
The restic-wrapper
command is designed to manually run Restic from the
command line. It can help to restore individual files and directories, or
run maintenance commands on remote Restic repositories.
The command can be invoked from any agent environment. Print its inline help with this command:
runagent restic-wrapper --help
Some options require a module backup ID, or backup destination UUID. Use
the --show
option to list them. For example:
runagent -m mail1 restic-wrapper --show
Example of output:
Destinations:
- dac5d576-ed63-5c4b-b028-c5e97022b27b OVH S3 destination (s3:s3.de.io.cloud.ovh.net/ns8-backups)
- 14030a59-a4e6-57cc-b8ea-cd5f97fe44c8 BackBlaze repo1 (b2:ns8-backups)
Scheduled backups:
- 1 Backup to BackBlaze repo1, destination UUID 14030a59-a4e6-57cc-b8ea-cd5f97fe44c8