selfhostblocks/modules/contracts/databasebackup/docs/default.md
2025-01-22 20:42:14 +01:00

3.9 KiB

Database Backup Contract

This NixOS contract represents a backup job that will backup everything in one database on a regular schedule.

It is a contract between a service that has database dumps to be backed up and a service that backs up databases dumps.

Contract Reference

These are all the options that are expected to exist for this contract to be respected.

id-prefix: contracts-databasebackup-options-
list-id: selfhostblocks-options
source: @OPTIONS_JSON@

Usage

What this contract defines is, from the user perspective - that is you - an implementation detail but it will at least define how to create a database dump, the user to backup with and how to restore from a database dump.

A NixOS module that can be backed up using this contract will provide a databasebackup option. Such a service is a requester which has a request.

Here is an example module defining such a databasebackup option:

{
  options = {
    databasebackupservices.instances = mkOption {
      description = ''
        Backup configuration.
      '';

      default = {};
      type = submodule {
        options = contracts.databasebackup.mkRequester {
          user = "postgres";

          backupName = "postgres.sql";

          backupCmd = ''
            ${pkgs.postgresql}/bin/pg_dumpall | ${pkgs.gzip}/bin/gzip --rsyncable
          '';

          restoreCmd = ''
            ${pkgs.gzip}/bin/gunzip | ${pkgs.postgresql}/bin/psql postgres
          '';
        };
      };
    };
  };
};

Now, on the other side we have a service that uses this backup option and actually backs up files. This service is a provider of this contract and will provide a result option.

{
  options = {
    instances = mkOption {
      description = "Files to backup.";
      default = {};
      type = attrsOf (submodule ({ name, config, ... }: {
        options = contracts.backup.mkProvider {
          settings = mkOption {
            description = ''
              Settings specific to the this provider.
            '';

            type = submodule {
              options = {
                enable = mkEnableOption "this backup intance.";
                # ... Other options specific to this provider.
              };
            };
          };

          resultCfg = let 
            fullName = name: repository: "backups-${name}_${repository.path}";
          in {
            restoreScript = fullName name config.settings.repository;
            restoreScriptText = "${fullName "<name>" { path = "path/to/repository"; }}";

            backupService = "${fullName name config.settings.repository}.service";
            backupServiceText = "${fullName "<name>" { path = "path/to/repository"; }}.service";
          };
        };
      }));
    };
  };
}

Then, to actually backup the myservice service, one would need to link the requester to the provider with:

databasebackupservice.instances.myservice = {
  request = config.myservice.databasebackup.request;
  
  settings = {
    enable = true;

    # ... Other options specific to this provider.
  };
};

It is advised to backup files to different location, to improve redundancy. Thanks to using contracts, this can be done easily either with the same databasebackupservice:

databasebackupservice.instances.myservice_2 = {
  request = myservice.backup;
  
  settings = {
    enable = true;
  
    repository = {
      path = "<remote path>";
    };
  };
};

Or with another module databasebackupservice_2!

Providers of the Database Backup Contract

Requester Blocks and Services