blob: 0724053a29819fd43bccf89a31482398a7b8b2e7 (
plain) (
tree)
|
|
# Some examples for how to use this module
#
# Host with media files - backup /media only locally
# my.modules.backups = {
# enable = true;
# passwordFile = config.age.secrets.restic_password.path
# local.paths = [ "/media" "/home" "/var/lib/important" ];
# remote.paths = [ "/home" "/var/lib/important" ]; # Excludes /media
# };
#
# Another example - different exclusions for local vs remote
# my.modules.backups = {
# enable = true;
# passwordFile = config.age.secrets.restic_password.path
# local.paths = [ "/home" "/var/cache/downloads" ];
# local.exclude = [ "*.tmp" ];
# remote.paths = [ "/home" ]; # Skip cache directory for remote
# remote.exclude = [ "*.tmp" "*.log" ]; # More aggressive exclusions for remote
# };
{
pkgs,
config,
lib,
...
}:
let
cfg = config.my.modules.backups;
# Helper scripts for easy backup access
restic-local = pkgs.writeShellScriptBin "restic-local" ''
export RESTIC_REPOSITORY="${cfg.localBasePath}/${config.networking.hostName}"
export RESTIC_PASSWORD_FILE="${cfg.passwordFile}"
exec ${pkgs.restic}/bin/restic "$@"
'';
restic-remote = pkgs.writeShellScriptBin "restic-remote" ''
export RESTIC_REPOSITORY="${cfg.remoteBaseRepository}:/${config.networking.hostName}/"
export RESTIC_PASSWORD_FILE="${cfg.passwordFile}"
${lib.optionalString (cfg.remote.environmentFile != null) ''
source ${cfg.remote.environmentFile}
''}
exec ${pkgs.restic}/bin/restic "$@"
'';
# Common backup options shared between local and remote
backupOptions = {
paths = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Paths to backup";
example = [
"/home"
"/var/lib/important-data"
];
};
exclude = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Paths to exclude from backup";
example = [
"*.tmp"
"/var/cache"
];
};
extraBackupArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"--exclude-caches"
"--compression=max"
];
description = "Additional arguments to pass to restic backup";
};
pruneOpts = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"--keep-daily 7"
"--keep-weekly 4"
"--keep-monthly 3"
];
description = "Pruning options for old backups";
};
timerConfig = lib.mkOption {
type = lib.types.attrs;
default = {
OnCalendar = "daily";
RandomizedDelaySec = "5m";
};
description = "Systemd timer configuration";
};
};
in
{
options.my.modules.backups = {
enable = lib.mkEnableOption "backups";
passwordFile = lib.mkOption {
type = lib.types.str;
default = config.age.secrets.restic_password.path;
description = "Path to file containing restic repository password";
example = "/run/secrets/restic-password";
};
localBasePath = lib.mkOption {
type = lib.types.str;
default = "/data/backups";
description = "Base path for local backup repositories";
example = "/mnt/backup-drive/backups";
};
remoteBaseRepository = lib.mkOption {
type = lib.types.str;
default = "gs:fcuny-infra-backups";
description = "Base repository URL for remote backups";
example = "s3:my-backup-bucket";
};
local = backupOptions;
remote = backupOptions // {
timerConfig = lib.mkOption {
type = lib.types.attrs;
default = {
OnCalendar = "daily";
# No randomized delay for remote to avoid overlap with local
};
description = "Systemd timer configuration for remote backups";
};
googleProjectId = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "fcuny-infra";
description = "Google Cloud project ID for GCS backups";
example = "my-backup-project";
};
googleCredentialsFile = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = config.age.secrets.restic_gcs_credentials.path;
description = "Path to Google Cloud service account credentials file";
example = "/run/secrets/gcs-credentials";
};
environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default =
if cfg.remote.googleProjectId != null && cfg.remote.googleCredentialsFile != null then
pkgs.writeText "restic-gcs-env" ''
GOOGLE_PROJECT_ID=${cfg.remote.googleProjectId}
GOOGLE_APPLICATION_CREDENTIALS=${cfg.remote.googleCredentialsFile}
''
else
null;
description = "Environment file for remote backup authentication";
};
};
helpers = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Install helper scripts (restic-local, restic-remote)";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages =
[
pkgs.restic
]
++ lib.optionals cfg.helpers [
restic-local
restic-remote
];
services.restic.backups = lib.mkMerge [
# Local backup configuration - only if paths are specified
(lib.mkIf (cfg.local.paths != [ ]) {
local = {
initialize = true;
repository = "${cfg.localBasePath}/${config.networking.hostName}";
passwordFile = cfg.passwordFile;
paths = cfg.local.paths;
exclude = cfg.local.exclude;
extraBackupArgs = cfg.local.extraBackupArgs;
timerConfig = cfg.local.timerConfig;
pruneOpts = cfg.local.pruneOpts;
};
})
# Remote backup configuration - only if paths are specified
(lib.mkIf (cfg.remote.paths != [ ]) {
remote =
{
initialize = true;
repository = "${cfg.remoteBaseRepository}:/${config.networking.hostName}/";
passwordFile = cfg.passwordFile;
paths = cfg.remote.paths;
exclude = cfg.remote.exclude;
extraBackupArgs = cfg.remote.extraBackupArgs;
timerConfig = cfg.remote.timerConfig;
pruneOpts = cfg.remote.pruneOpts;
}
// lib.optionalAttrs (cfg.remote.environmentFile != null) {
environmentFile = toString cfg.remote.environmentFile;
};
})
];
};
}
|