backup: do not make systemd service wait for backup to complete

fixes #421
This commit is contained in:
ibizaman 2026-04-22 23:44:48 +02:00 committed by Pierre Penninckx
parent 4855404f5d
commit 96a8753548
8 changed files with 97 additions and 11 deletions

View file

@ -347,6 +347,12 @@
"blocks-borgbackup-maintenance": [ "blocks-borgbackup-maintenance": [
"blocks-borgbackup.html#blocks-borgbackup-maintenance" "blocks-borgbackup.html#blocks-borgbackup-maintenance"
], ],
"blocks-borgbackup-maintenance-manuql": [
"blocks-borgbackup.html#blocks-borgbackup-maintenance-manuql"
],
"blocks-borgbackup-maintenance-restore": [
"blocks-borgbackup.html#blocks-borgbackup-maintenance-restore"
],
"blocks-borgbackup-maintenance-troubleshooting": [ "blocks-borgbackup-maintenance-troubleshooting": [
"blocks-borgbackup.html#blocks-borgbackup-maintenance-troubleshooting" "blocks-borgbackup.html#blocks-borgbackup-maintenance-troubleshooting"
], ],
@ -1373,6 +1379,12 @@
"blocks-restic-maintenance": [ "blocks-restic-maintenance": [
"blocks-restic.html#blocks-restic-maintenance" "blocks-restic.html#blocks-restic-maintenance"
], ],
"blocks-restic-maintenance-manuql": [
"blocks-restic.html#blocks-restic-maintenance-manuql"
],
"blocks-restic-maintenance-restore": [
"blocks-restic.html#blocks-restic-maintenance-restore"
],
"blocks-restic-maintenance-troubleshooting": [ "blocks-restic-maintenance-troubleshooting": [
"blocks-restic.html#blocks-restic-maintenance-troubleshooting" "blocks-restic.html#blocks-restic-maintenance-troubleshooting"
], ],

View file

@ -386,8 +386,13 @@ in
${serviceName} = mkMerge [ ${serviceName} = mkMerge [
{ {
serviceConfig = { serviceConfig = {
# Makes the systemd service wait for the backup to be done before changing state to inactive. # Purposely not a oneshot systemd service otherwise
Type = "oneshot"; # the service waits on the completion the backup before deactivating.
# This seems like a nice property at first but there is one annoying
# edge case when deploying. If a backup job somehow is started when
# the deploy happens, the deploy will wait on the service to finish
# before considering the deploy done. And worse, it will consider the
# deploy as failed if the backup fails, which is not what you want.
Nice = lib.mkForce cfg.performance.niceness; Nice = lib.mkForce cfg.performance.niceness;
IOSchedulingClass = lib.mkForce cfg.performance.ioSchedulingClass; IOSchedulingClass = lib.mkForce cfg.performance.ioSchedulingClass;
IOSchedulingPriority = lib.mkForce cfg.performance.ioPriority; IOSchedulingPriority = lib.mkForce cfg.performance.ioPriority;
@ -414,6 +419,7 @@ in
in in
{ {
script = script.preStart; script = script.preStart;
# Makes the systemd service wait for the backup to be done before changing state to inactive.
serviceConfig.Type = "oneshot"; serviceConfig.Type = "oneshot";
serviceConfig.LoadCredential = script.loadCredentials; serviceConfig.LoadCredential = script.loadCredentials;
} }

View file

@ -3,6 +3,7 @@
Defined in [`/modules/blocks/borgbackup.nix`](@REPO@/modules/blocks/borgbackup.nix). Defined in [`/modules/blocks/borgbackup.nix`](@REPO@/modules/blocks/borgbackup.nix).
This block sets up a backup job using [BorgBackup][]. This block sets up a backup job using [BorgBackup][].
It is heavily based on the nixpkgs BorgBackup module.
[borgbackup]: https://www.borgbackup.org/ [borgbackup]: https://www.borgbackup.org/
@ -229,6 +230,36 @@ See [Backups Dashboard and Alert](blocks-monitoring.html#blocks-monitoring-backu
## Maintenance {#blocks-borgbackup-maintenance} ## Maintenance {#blocks-borgbackup-maintenance}
### Manual Backup {#blocks-borgbackup-maintenance-manuql}
To launch a backup manually, just run:
```bash
systemctl start <systemd-service-name>
```
You can easily discover the systemd service name you need by either listing the units:
```bash
systemctl list-units 'borgbackup*'
```
Or by autocompleting the unit name with `<TAB>`:
```bash
systemctl start borgbackup<TAB><TAB>
```
Note that the systemd services are of `Type=simple` which means the systemd service
will not wait for the backup completion to terminate.
If you want instead to wait for the backup to complete, use the `--wait` flag:
```bash
systemctl start --wait <systemd-service-name>
```
### Restore {#blocks-borgbackup-maintenance-restore}
One command-line helper is provided per backup instance and repository pair to automatically supply the needed secrets. One command-line helper is provided per backup instance and repository pair to automatically supply the needed secrets.
The restore script has all the secrets needed to access the repo, The restore script has all the secrets needed to access the repo,

View file

@ -413,7 +413,13 @@ in
nameValuePair "${fullName name instance.settings.repository}_restore_gen" { nameValuePair "${fullName name instance.settings.repository}_restore_gen" {
enable = true; enable = true;
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig.Type = "oneshot"; # Purposely not a oneshot systemd service otherwise
# the service waits on the completion the backup before deactivating.
# This seems like a nice property at first but there is one annoying
# edge case when deploying. If a backup job somehow is started when
# the deploy happens, the deploy will wait on the service to finish
# before considering the deploy done. And worse, it will consider the
# deploy as failed if the backup fails, which is not what you want.
script = ( script = (
shb.replaceSecrets { shb.replaceSecrets {
userConfig = instance.settings.repository.secrets // { userConfig = instance.settings.repository.secrets // {

View file

@ -3,12 +3,12 @@
Defined in [`/modules/blocks/restic.nix`](@REPO@/modules/blocks/restic.nix). Defined in [`/modules/blocks/restic.nix`](@REPO@/modules/blocks/restic.nix).
This block sets up a backup job using [Restic][]. This block sets up a backup job using [Restic][].
It is heavily based on the nixpkgs Restic module.
[restic]: https://restic.net/ [restic]: https://restic.net/
## Provider Contracts {#blocks-restic-contract-provider} ## Provider Contracts {#blocks-restic-contract-provider}
This block provides the following contracts: This block provides the following contracts:
- [backup contract](contracts-backup.html) under the [`shb.restic.instances`][instances] option. - [backup contract](contracts-backup.html) under the [`shb.restic.instances`][instances] option.
@ -26,7 +26,6 @@ a backup Systemd service and a [restore script](#blocks-restic-maintenance) are
## Usage {#blocks-restic-usage} ## Usage {#blocks-restic-usage}
The following examples assume usage of the [sops block][] to provide secrets The following examples assume usage of the [sops block][] to provide secrets
although any blocks providing the [secrets contract][] works too. although any blocks providing the [secrets contract][] works too.
@ -35,7 +34,6 @@ although any blocks providing the [secrets contract][] works too.
### One folder backed up manually {#blocks-restic-usage-provider-manual} ### One folder backed up manually {#blocks-restic-usage-provider-manual}
The following snippet shows how to configure The following snippet shows how to configure
the backup of 1 folder to 1 repository. the backup of 1 folder to 1 repository.
We assume that the folder `/var/lib/myfolder` of the service `myservice` must be backed up. We assume that the folder `/var/lib/myfolder` of the service `myservice` must be backed up.
@ -232,11 +230,44 @@ See [Backups Dashboard and Alert](blocks-monitoring.html#blocks-monitoring-backu
## Maintenance {#blocks-restic-maintenance} ## Maintenance {#blocks-restic-maintenance}
One command-line helper is provided per backup instance and repository pair to automatically supply the needed secrets. ### Manual Backup {#blocks-restic-maintenance-manuql}
To launch a backup manually, just run:
```bash
systemctl start <systemd-service-name>
```
You can easily discover the systemd service name you need by either listing the units:
```bash
systemctl list-units 'restic*'
```
Or by autocompleting the unit name with `<TAB>`:
```bash
systemctl start restic<TAB><TAB>
```
Note that the systemd services are of `Type=simple` which means the systemd service
will not wait for the backup completion to terminate.
If you want instead to wait for the backup to complete, use the `--wait` flag:
```bash
systemctl start --wait <systemd-service-name>
```
### Restore {#blocks-restic-maintenance-restore}
One command-line helper is provided per backup instance and repository pair which allows to:
- list snapshots: `<script> snapshots`
- to restore a snapshot: `<script> restore <snapshot>`
The restore script has all the secrets needed to access the repo, The restore script has all the secrets needed to access the repo,
it will run `sudo` automatically it will run `sudo` automatically
and the user running it needs to have correct permissions for privilege escalation and the user running it needs to have correct permissions for privilege escalation.
In the [multiple directories example](#blocks-restic-usage-multiple) above, the following 6 helpers are provided in the `$PATH`: In the [multiple directories example](#blocks-restic-usage-multiple) above, the following 6 helpers are provided in the `$PATH`:

View file

@ -105,7 +105,7 @@ shb.test.runNixOSTest {
with subtest("First backup in repo"): with subtest("First backup in repo"):
print(machine.succeed("systemctl cat ${provider.backupService}")) print(machine.succeed("systemctl cat ${provider.backupService}"))
machine.succeed("systemctl start ${provider.backupService}") machine.succeed("systemctl start --wait ${provider.backupService}")
with subtest("New content"): with subtest("New content"):
for path in sourceDirectories: for path in sourceDirectories:

View file

@ -85,7 +85,7 @@ shb.test.runNixOSTest {
with subtest("backup"): with subtest("backup"):
print(machine.succeed("systemctl cat ${provider.result.backupService}")) print(machine.succeed("systemctl cat ${provider.result.backupService}"))
print(machine.succeed("ls -l /run/hardcodedsecrets/hardcodedsecret_passphrase")) print(machine.succeed("ls -l /run/hardcodedsecrets/hardcodedsecret_passphrase"))
machine.succeed("systemctl start ${provider.result.backupService}") machine.succeed("systemctl start --wait ${provider.result.backupService}")
with subtest("drop database"): with subtest("drop database"):
machine.succeed(peer_cmd("DROP DATABASE ${database}", db="postgres")) machine.succeed(peer_cmd("DROP DATABASE ${database}", db="postgres"))

View file

@ -143,7 +143,7 @@ let
}) })
with subtest("First backup in repo A"): with subtest("First backup in repo A"):
machine.succeed("systemctl start ${backupService}") machine.succeed("systemctl start --wait ${backupService}")
with subtest("New content"): with subtest("New content"):
machine.succeed(""" machine.succeed("""