From 0fa4a42be7b5f9d877130e9fa5b3498e3a59ba61 Mon Sep 17 00:00:00 2001
From: Pierre Penninckx <github@pierre.tiserbox.com>
Date: Tue, 20 Aug 2024 07:33:13 -0700
Subject: [PATCH] switch all modules to backup block (#279)

---
 modules/blocks/ldap.nix                      | 30 ++++++--
 modules/contracts/backup/docs/default.md     |  4 +-
 modules/services/arr.nix                     | 73 ++++++--------------
 modules/services/audiobookshelf.nix          | 29 ++++++--
 modules/services/deluge.nix                  | 45 +++++++++---
 modules/services/grocy.nix                   | 29 ++++++--
 modules/services/hledger.nix                 | 38 ++++++++--
 modules/services/home-assistant.nix          | 40 ++++++-----
 modules/services/jellyfin.nix                | 30 ++++++--
 modules/services/nextcloud-server.nix        | 47 +++++++++----
 modules/services/vaultwarden.nix             |  4 +-
 modules/services/vaultwarden/docs/default.md | 18 ++++-
 test/blocks/authelia.nix                     |  5 --
 test/blocks/ldap.nix                         |  1 -
 test/common.nix                              |  6 --
 test/modules/arr.nix                         | 24 +------
 test/modules/nginx.nix                       |  4 +-
 17 files changed, 264 insertions(+), 163 deletions(-)

diff --git a/modules/blocks/ldap.nix b/modules/blocks/ldap.nix
index 782775c..48cee98 100644
--- a/modules/blocks/ldap.nix
+++ b/modules/blocks/ldap.nix
@@ -87,6 +87,30 @@ in
       readOnly = true;
       default = { path = "/var/lib/lldap"; };
     };
+
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."lldap" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.lldap.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          "/var/lib/lldap"
+        ];
+      };
+    };
   };
 
   
@@ -143,11 +167,5 @@ in
         verbose = cfg.debug;
       };
     };
-
-    shb.backup.instances.lldap = {
-      sourceDirectories = [
-        "/var/lib/lldap"
-      ];
-    };
   };
 }
diff --git a/modules/contracts/backup/docs/default.md b/modules/contracts/backup/docs/default.md
index 4e8d790..e0ba5d0 100644
--- a/modules/contracts/backup/docs/default.md
+++ b/modules/contracts/backup/docs/default.md
@@ -34,7 +34,7 @@ Then, to actually backup the service, one would write:
 shb.<backup_impl>.instances."<service>" = shb.<service>.backup // {
   enable = true;
 
-  // Options specific to backup_impl
+  # Options specific to backup_impl
 };
 ```
 
@@ -44,7 +44,7 @@ Then, for extra caution, a second backup could be made using another module `shb
 shb.<backup_impl_2>.instances."<service>" = shb.<service>.backup // {
   enable = true;
 
-  // Options specific to backup_impl_2
+  # Options specific to backup_impl_2
 };
 ```
 
diff --git a/modules/services/arr.nix b/modules/services/arr.nix
index 95a0f41..3cbb4cc 100644
--- a/modules/services/arr.nix
+++ b/modules/services/arr.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, lib, ... }:
+{ config, options, pkgs, lib, ... }:
 
 let
   cfg = config.shb.arr;
@@ -329,13 +329,28 @@ let
           example = "https://authelia.example.com";
         };
 
-        backupCfg = lib.mkOption {
-          type = lib.types.anything;
-          description = "Backup configuration for ${name}.";
-          default = {};
-          example = {
-            backend = "restic";
-            repositories = [];
+        backup = lib.mkOption {
+          type = contracts.backup;
+          description = ''
+            Backup configuration. This is an output option.
+
+            Use it to initialize a block implementing the "backup" contract.
+            For example, with the restic block:
+
+            ```
+            shb.restic.instances."${name}" = {
+              enable = true;
+
+              # Options specific to Restic.
+            } // config.shb.${name}.backup;
+            ```
+          '';
+          readOnly = true;
+          default = {
+            sourceDirectories = [
+              cfg.${name}.dataDir
+            ];
+            excludePatterns = [".db-shm" ".db-wal" ".mono"];
           };
         };
       } // (c.moreOptions or {});
@@ -370,13 +385,6 @@ in
       };
 
       shb.nginx.vhosts = [ (vhosts {} cfg') ];
-
-      shb.backup.instances.radarr = cfg'.backupCfg // {
-        sourceDirectories = [
-          cfg'.dataDir
-        ];
-        excludePatterns = [".db-shm" ".db-wal" ".mono"];
-      };
     }))
     (lib.mkIf cfg.radarr.enable (backup "radarr"))
 
@@ -407,13 +415,6 @@ in
       };
 
       shb.nginx.vhosts = [ (vhosts {} cfg') ];
-
-      shb.backup.instances.sonarr = cfg'.backupCfg // {
-        sourceDirectories = [
-          cfg'.dataDir
-        ];
-        excludePatterns = [".db-shm" ".db-wal" ".mono"];
-      };
     }))
     (lib.mkIf cfg.sonarr.enable (backup "sonarr"))
 
@@ -441,13 +442,6 @@ in
       };
 
       shb.nginx.vhosts = [ (vhosts {} cfg') ];
-
-      shb.backup.instances.bazarr = cfg'.backupCfg // {
-        sourceDirectories = [
-          cfg'.dataDir
-        ];
-        excludePatterns = [".db-shm" ".db-wal" ".mono"];
-      };
     }))
     (lib.mkIf cfg.bazarr.enable (backup "bazarr"))
 
@@ -470,13 +464,6 @@ in
       };
 
       shb.nginx.vhosts = [ (vhosts {} cfg') ];
-
-      shb.backup.instances.readarr = cfg'.backupCfg // {
-        sourceDirectories = [
-          cfg'.dataDir
-        ];
-        excludePatterns = [".db-shm" ".db-wal" ".mono"];
-      };
     }))
     (lib.mkIf cfg.readarr.enable (backup "readarr"))
 
@@ -504,13 +491,6 @@ in
       };
 
       shb.nginx.vhosts = [ (vhosts {} cfg') ];
-
-      shb.backup.instances.lidarr = cfg'.backupCfg // {
-        sourceDirectories = [
-          cfg'.dataDir
-        ];
-        excludePatterns = [".db-shm" ".db-wal" ".mono"];
-      };
     }))
     (lib.mkIf cfg.lidarr.enable (backup "lidarr"))
 
@@ -535,13 +515,6 @@ in
       shb.nginx.vhosts = [ (vhosts {
         extraBypassResources = [ "^/dl.*" ];
       } cfg') ];
-
-      shb.backup.instances.jackett = cfg'.backupCfg // {
-        sourceDirectories = [
-          cfg'.dataDir
-        ];
-        excludePatterns = [".db-shm" ".db-wal" ".mono"];
-      };
     }))
     (lib.mkIf cfg.jackett.enable (backup "jackett"))
   ];
diff --git a/modules/services/audiobookshelf.nix b/modules/services/audiobookshelf.nix
index c9a1140..d91a2df 100644
--- a/modules/services/audiobookshelf.nix
+++ b/modules/services/audiobookshelf.nix
@@ -82,6 +82,30 @@ in
       description = "File containing the SSO shared secret.";
     };
 
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."audiobookshelf" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.audiobookshelf.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          "/var/lib/audiobookshelf"
+        ];
+      };
+    };
+
     logLevel = lib.mkOption {
       type = lib.types.nullOr (lib.types.enum ["critical" "error" "warning" "info" "debug"]);
       description = "Enable logging.";
@@ -149,11 +173,6 @@ in
     # We backup the whole audiobookshelf directory and set permissions for the backup user accordingly.
     users.groups.audiobookshelf.members = [ "backup" ];
     users.groups.media.members = [ "backup" ];
-    shb.backup.instances.audiobookshelf = {
-      sourceDirectories = [
-        /var/lib/${config.services.audiobookshelf.dataDir}
-      ];
-    };
   } {
     systemd.services.audiobookshelfd.serviceConfig = cfg.extraServiceConfig;
   }]);
diff --git a/modules/services/deluge.nix b/modules/services/deluge.nix
index cc24b2c..4efd3c1 100644
--- a/modules/services/deluge.nix
+++ b/modules/services/deluge.nix
@@ -39,6 +39,12 @@ in
       default = null;
     };
 
+    dataDir = lib.mkOption {
+      type = lib.types.str;
+      description = "Path where all configuration and state is stored.";
+      default = "/var/lib/deluge";
+    };
+
     daemonPort = lib.mkOption {
       type = lib.types.int;
       description = "Deluge daemon port";
@@ -227,6 +233,30 @@ in
       '';
     };
 
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."vaultwarden" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.vaultwarden.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          cfg.dataDir
+        ];
+      };
+    };
+
     logLevel = lib.mkOption {
       type = lib.types.nullOr (lib.types.enum ["critical" "error" "warning" "info" "debug"]);
       description = "Enable logging.";
@@ -240,6 +270,8 @@ in
       enable = true;
       declarative = true;
       openFirewall = true;
+      inherit (cfg) dataDir;
+
       config = {
         download_location = cfg.settings.downloadLocation;
         allow_remote = true;
@@ -285,7 +317,7 @@ in
         new_release_check = false;
       };
 
-      authFile = "${config.services.deluge.dataDir}/.config/deluge/authTemplate";
+      authFile = "${cfg.dataDir}/.config/deluge/authTemplate";
 
       web.enable = true;
       web.port = cfg.webPort;
@@ -297,14 +329,14 @@ in
       } // (lib.optionalAttrs (config.shb.deluge.prometheusScraperPasswordFile != null) {
         prometheus_scraper.password.source = config.shb.deluge.prometheusScraperPasswordFile;
       });
-      resultPath = "${config.services.deluge.dataDir}/.config/deluge/authTemplate";
+      resultPath = "${cfg.dataDir}/.config/deluge/authTemplate";
       generator = name: value: pkgs.writeText "delugeAuth" (authGenerator value);
     });
 
     systemd.services.deluged.serviceConfig.ExecStart = lib.mkForce (lib.concatStringsSep " \\\n    " ([
       "${config.services.deluge.package}/bin/deluged"
       "--do-not-daemonize"
-      "--config ${config.services.deluge.dataDir}/.config/deluge"
+      "--config ${cfg.dataDir}/.config/deluge"
     ] ++ (lib.optional (!(isNull cfg.logLevel)) "-L ${cfg.logLevel}")
     ));
     
@@ -316,7 +348,7 @@ in
         };
       in
         [
-          "L+ ${config.services.deluge.dataDir}/.config/deluge/plugins - - - - ${plugins}"
+          "L+ ${cfg.dataDir}/.config/deluge/plugins - - - - ${plugins}"
         ];
 
     shb.nginx.vhosts = [
@@ -352,11 +384,6 @@ in
     # We backup the whole deluge directory and set permissions for the backup user accordingly.
     users.groups.deluge.members = [ "backup" ];
     users.groups.media.members = [ "backup" ];
-    shb.backup.instances.deluge = {
-      sourceDirectories = [
-        config.services.deluge.dataDir
-      ];
-    };
   } {
     systemd.services.deluged.serviceConfig = cfg.extraServiceConfig;
   } (lib.mkIf (config.shb.deluge.prometheusScraperPasswordFile != null) {
diff --git a/modules/services/grocy.nix b/modules/services/grocy.nix
index de6968b..6c36d33 100644
--- a/modules/services/grocy.nix
+++ b/modules/services/grocy.nix
@@ -62,6 +62,30 @@ in
       '';
     };
 
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."grocy" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.grocy.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          cfg.dataDir
+        ];
+      };
+    };
+
     logLevel = lib.mkOption {
       type = lib.types.nullOr (lib.types.enum ["critical" "error" "warning" "info" "debug"]);
       description = "Enable logging.";
@@ -95,11 +119,6 @@ in
     # We backup the whole grocy directory and set permissions for the backup user accordingly.
     users.groups.grocy.members = [ "backup" ];
     users.groups.media.members = [ "backup" ];
-    shb.backup.instances.grocy = {
-      sourceDirectories = [
-        config.services.grocy.dataDir
-      ];
-    };
   } {
     systemd.services.grocyd.serviceConfig = cfg.extraServiceConfig;
   }]);
diff --git a/modules/services/hledger.nix b/modules/services/hledger.nix
index b4bb9da..335c8ee 100644
--- a/modules/services/hledger.nix
+++ b/modules/services/hledger.nix
@@ -23,6 +23,12 @@ in
       example = "mydomain.com";
     };
 
+    dataDir = lib.mkOption {
+      description = "Folder where Hledger will store all its data.";
+      type = lib.types.str;
+      default = "/var/lib/hledger";
+    };
+
     ssl = lib.mkOption {
       description = "Path to SSL files";
       type = lib.types.nullOr contracts.ssl.certs;
@@ -47,6 +53,30 @@ in
       description = "OIDC endpoint for SSO";
       example = "https://authelia.example.com";
     };
+
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."hledger" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.hledger.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          cfg.dataDir
+        ];
+      };
+    };
   };
 
   config = lib.mkIf cfg.enable {
@@ -55,7 +85,7 @@ in
       # Must be empty otherwise it repeats the fqdn, we get something like https://${fqdn}/${fqdn}/
       baseUrl = "";
 
-      stateDir = "/var/lib/hledger";
+      stateDir = cfg.dataDir;
       journalFiles = ["hledger.journal"];
 
       host = "127.0.0.1";
@@ -89,11 +119,5 @@ in
         }];
       }
     ];
-
-    shb.backup.instances.hledger = {
-      sourceDirectories = [
-        config.services.hledger-web.stateDir
-      ];
-    };
   };
 }
diff --git a/modules/services/home-assistant.nix b/modules/services/home-assistant.nix
index 5b5424b..c9f852e 100644
--- a/modules/services/home-assistant.nix
+++ b/modules/services/home-assistant.nix
@@ -136,13 +136,28 @@ in
       };
     };
 
-    backupCfg = lib.mkOption {
-      type = lib.types.anything;
-      description = "Backup configuration for home-assistant";
-      default = {};
-      example = {
-        backend = "restic";
-        repositories = [];
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."home-assistant" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.home-assistant.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        # No need for backup hooks as we use an hourly automation job in home assistant directly with a cron job.
+        sourceDirectories = [
+          "/var/lib/hass/backups"
+        ];
       };
     };
   };
@@ -308,17 +323,6 @@ in
       "f ${config.services.home-assistant.configDir}/scripts.yaml     0755 hass hass"
     ];
 
-    shb.backup.instances.home-assistant = lib.mkIf (cfg.backupCfg != {}) (
-      cfg.backupCfg
-      // {
-        sourceDirectories = [
-          "${config.services.home-assistant.configDir}/backups"
-        ];
-
-        # No need for backup hooks as we use an hourly automation job in home assistant directly with a cron job.
-      }
-    );
-
     # Adds the "backup" user to the "hass" group.
     users.groups.hass = {
       members = [ "backup" ];
diff --git a/modules/services/jellyfin.nix b/modules/services/jellyfin.nix
index dcc1450..56f64ba 100644
--- a/modules/services/jellyfin.nix
+++ b/modules/services/jellyfin.nix
@@ -119,6 +119,30 @@ in
         };
       };
     };
+
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."jellyfin" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.jellyfin.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          "/var/lib/jellyfin"
+        ];
+      };
+    };
   };
 
   config = lib.mkIf cfg.enable {
@@ -250,12 +274,6 @@ in
         '';
     };
 
-    shb.backup.instances.jellyfin = {
-      sourceDirectories = [
-        "/var/lib/jellyfin"
-      ];
-    };
-
     services.prometheus.scrapeConfigs = [{
       job_name = "jellyfin";
       static_configs = [
diff --git a/modules/services/nextcloud-server.nix b/modules/services/nextcloud-server.nix
index a91a07e..c252164 100644
--- a/modules/services/nextcloud-server.nix
+++ b/modules/services/nextcloud-server.nix
@@ -1,7 +1,8 @@
-{ config, pkgs, lib, ... }:
+{ config, options, pkgs, lib, ... }:
 
 let
   cfg = config.shb.nextcloud;
+  opt = options.shb.nextcloud;
 
   fqdn = "${cfg.subdomain}.${cfg.domain}";
   fqdnWithPort = if isNull cfg.port then fqdn else "${fqdn}:${toString cfg.port}";
@@ -496,6 +497,32 @@ in
       '';
     };
 
+
+    backup = lib.mkOption {
+      type = contracts.backup;
+      description = ''
+        Backup configuration. This is an output option.
+
+        Use it to initialize a block implementing the "backup" contract.
+        For example, with the restic block:
+
+        ```
+        shb.restic.instances."nextcloud" = {
+          enable = true;
+
+          # Options specific to Restic.
+        } // config.shb.nextcloud.backup;
+        ```
+      '';
+      readOnly = true;
+      default = {
+        sourceDirectories = [
+          cfg.dataDir
+        ];
+        excludePatterns = [".rnd"];
+      };
+    };
+
     debug = lib.mkOption {
       type = lib.types.bool;
       description = "Enable more verbose logging.";
@@ -542,11 +569,11 @@ in
         };
       };
 
-      users.groups = {
-        nextcloud = {
-          members = [ "backup" ];
-        };
-      };
+      # users.groups = {
+      #   nextcloud = {
+      #     members = [ "backup" ];
+      #   };
+      # };
 
       # LDAP is manually configured through
       # https://github.com/lldap/lldap/blob/main/example_configs/nextcloud.md, see also
@@ -700,14 +727,6 @@ in
 
       systemd.services.nextcloud-setup.requires = cfg.mountPointServices;
       systemd.services.nextcloud-setup.after = cfg.mountPointServices;
-
-      # Sets up backup for Nextcloud.
-      shb.backup.instances.nextcloud = {
-        sourceDirectories = [
-          cfg.dataDir
-        ];
-        excludePatterns = [".rnd"];
-      };
     })
 
     (lib.mkIf cfg.apps.onlyoffice.enable {
diff --git a/modules/services/vaultwarden.nix b/modules/services/vaultwarden.nix
index 21ee588..ba2fa78 100644
--- a/modules/services/vaultwarden.nix
+++ b/modules/services/vaultwarden.nix
@@ -124,7 +124,9 @@ in
 
         ```
         shb.restic.instances."vaultwarden" = {
-          poolName = "root";
+          enable = true;
+
+          # Options specific to Restic.
         } // config.shb.vaultwarden.backup;
         ```
       '';
diff --git a/modules/services/vaultwarden/docs/default.md b/modules/services/vaultwarden/docs/default.md
index af1a6d1..f2b130e 100644
--- a/modules/services/vaultwarden/docs/default.md
+++ b/modules/services/vaultwarden/docs/default.md
@@ -9,7 +9,7 @@ This NixOS module is a service that sets up a [Vaultwarden Server](https://githu
 - Access through subdomain using reverse proxy.
 - Access through HTTPS using reverse proxy.
 - Automatic setup of Redis database for caching.
-- Backup of the data directory through the [backup block](./blocks-backup.html).
+- Backup of the data directory through the [backup contract](./contracts-backup.html).
 - [Integration Tests](@REPO@/test/services/vaultwarden.nix)
   - Tests /admin can only be accessed when authenticated with SSO.
 - Access to advanced options not exposed here thanks to how NixOS modules work.
@@ -92,13 +92,27 @@ shb.zfs.datasets."vaultwarden" = config.shb.vaultwarden.mount;
 shb.zfs.datasets."postgresql".path = "/var/lib/postgresql";
 ```
 
+### Backup {#services-vaultwarden-backup}
+
+Backing up Vaultwarden using the [Restic block](blocks-restic.html) is done like so:
+
+```nix
+shb.restic.instances."vaultwarden" = config.shb.vaultwarden.backup // {
+  enable = true;
+};
+```
+
+The name `"vaultwarden"` in the `instances` can be anything.
+The `config.shb.vaultwarden.backup` option provides what directories to backup.
+You can define any number of Restic instances to backup Vaultwarden multiple times.
+
 ## Maintenance {#services-vaultwarden-maintenance}
 
 No command-line tool is provided to administer Vaultwarden.
 
 Instead, the admin section can be found at the `/admin` endpoint.
 
-## Debug {#services-backup-debug}
+## Debug {#services-vaultwarden-debug}
 
 In case of an issue, check the logs of the `vaultwarden.service` systemd service.
 
diff --git a/test/blocks/authelia.nix b/test/blocks/authelia.nix
index d6fdda9..d1c9df0 100644
--- a/test/blocks/authelia.nix
+++ b/test/blocks/authelia.nix
@@ -12,11 +12,6 @@ in
       imports = [
         (pkgs'.path + "/nixos/modules/profiles/headless.nix")
         (pkgs'.path + "/nixos/modules/profiles/qemu-guest.nix")
-        {
-          options = {
-            shb.backup = lib.mkOption { type = lib.types.anything; };
-          };
-        }
         ../../modules/blocks/authelia.nix
         ../../modules/blocks/ldap.nix
         ../../modules/blocks/postgresql.nix
diff --git a/test/blocks/ldap.nix b/test/blocks/ldap.nix
index a6724ca..2b67cbd 100644
--- a/test/blocks/ldap.nix
+++ b/test/blocks/ldap.nix
@@ -13,7 +13,6 @@ in
         {
           options = {
             shb.ssl.enable = lib.mkEnableOption "ssl";
-            shb.backup = lib.mkOption { type = lib.types.anything; };
           };
         }
         ../../modules/blocks/ldap.nix
diff --git a/test/common.nix b/test/common.nix
index 88a1322..d1ed745 100644
--- a/test/common.nix
+++ b/test/common.nix
@@ -104,12 +104,6 @@ in
     imports =
       ( baseImports pkgs )
       ++ [
-        # TODO: replace this option by the backup contract
-        {
-          options = {
-            shb.backup = lib.mkOption { type = lib.types.anything; };
-          };
-        }
         # TODO: replace postgresql.nix and authelia.nix by the sso contract
         ../modules/blocks/postgresql.nix
         ../modules/blocks/authelia.nix
diff --git a/test/modules/arr.nix b/test/modules/arr.nix
index 2553ab6..7273a89 100644
--- a/test/modules/arr.nix
+++ b/test/modules/arr.nix
@@ -13,7 +13,6 @@ let
           {
             options = {
               systemd = anyOpt {};
-              shb.backup = anyOpt {};
               shb.nginx = anyOpt {};
               users = anyOpt {};
               services.nginx = anyOpt {};
@@ -34,14 +33,13 @@ let
     in {
       inherit (cfg) services users;
       systemd = systemdRedacted;
-      shb = { inherit (cfg.shb) backup nginx; };
+      shb = { inherit (cfg.shb) nginx; };
     };
 in
 {
   testArrNoOptions = {
     expected = {
       systemd = {};
-      shb.backup = {};
       shb.nginx = {};
       users = {};
       services.nginx = {};
@@ -67,16 +65,6 @@ in
       systemd.tmpfiles.rules = [
         "d '/var/lib/radarr' 0750 radarr radarr - -"
       ];
-      shb.backup.instances.radarr = {
-        excludePatterns = [
-          ".db-shm"
-          ".db-wal"
-          ".mono"
-        ];
-        sourceDirectories = [
-          "/var/lib/radarr"
-        ];
-      };
       shb.nginx.vhosts = [
         {
           autheliaRules = [
@@ -143,13 +131,6 @@ in
       systemd.tmpfiles.rules = [
         "d '/var/lib/radarr' 0750 radarr radarr - -"
       ];
-      shb.backup.instances = {
-        radarr = {
-          enable = true;
-          sourceDirectories = [ "/var/lib/radarr" ];
-          excludePatterns = [ ".db-shm" ".db-wal" ".mono" ];
-        };
-      };
       shb.nginx.vhosts = [
         {
           autheliaRules = [
@@ -201,9 +182,6 @@ in
         settings = {
           ApiKey.source = pkgs.writeText "key" "/run/radarr/apikey";
         };
-        backupCfg = {
-          enable = true;
-        };
       };
     };
   };
diff --git a/test/modules/nginx.nix b/test/modules/nginx.nix
index fab349f..6cf1e7f 100644
--- a/test/modules/nginx.nix
+++ b/test/modules/nginx.nix
@@ -17,7 +17,6 @@ let
               security = anyOpt {};
               services = anyOpt {};
               shb.authelia = anyOpt {};
-              shb.backup = anyOpt {};
               systemd = anyOpt {};
               users = anyOpt {};
             };
@@ -29,13 +28,12 @@ let
       }).config;
     in lib.attrsets.filterAttrsRecursive (n: v: n != "extraConfig") {
       inherit (cfg) services;
-      shb = { inherit (cfg.shb) backup nginx; };
+      shb = { inherit (cfg.shb) nginx; };
     };
 in
 {
   testNoOptions = {
     expected = {
-      shb.backup = {};
       shb.nginx = {
         accessLog = false;
         vhosts = [];