1 { config, pkgs, lib, ... }:
6 cfg = config.services.snapper;
10 options.services.snapper = {
12 snapshotRootOnBoot = mkOption {
15 description = lib.mdDoc ''
16 Whether to snapshot root on boot
20 snapshotInterval = mkOption {
23 description = lib.mdDoc ''
26 The format is described in
27 {manpage}`systemd.time(7)`.
31 cleanupInterval = mkOption {
34 description = lib.mdDoc ''
37 The format is described in
38 {manpage}`systemd.time(7)`.
43 type = types.nullOr types.lines;
45 description = lib.mdDoc ''
46 Global display difference filter. See man:snapper(8) for more details.
52 example = literalExpression ''
65 description = lib.mdDoc ''
66 Subvolume configuration
69 type = types.attrsOf (types.submodule {
71 subvolume = mkOption {
73 description = lib.mdDoc ''
74 Path of the subvolume or mount point.
75 This path is a subvolume and has to contain a subvolume named
77 See also man:snapper(8) section PERMISSIONS.
82 type = types.enum [ "btrfs" ];
84 description = lib.mdDoc ''
85 Filesystem type. Only btrfs is stable and tested.
89 extraConfig = mkOption {
92 description = lib.mdDoc ''
93 Additional configuration next to SUBVOLUME and FSTYPE.
94 See man:snapper-configs(5).
102 config = mkIf (cfg.configs != {}) (let
103 documentation = [ "man:snapper(8)" "man:snapper-configs(5)" ];
108 systemPackages = [ pkgs.snapper ];
110 # Note: snapper/config-templates/default is only needed for create-config
111 # which is not the NixOS way to configure.
114 "sysconfig/snapper".text = ''
115 SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
119 // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({
121 ${subvolume.extraConfig}
122 FSTYPE="${subvolume.fstype}"
123 SUBVOLUME="${subvolume.subvolume}"
126 // (lib.optionalAttrs (cfg.filters != null) {
127 "snapper/filters/default.txt".text = cfg.filters;
132 services.dbus.packages = [ pkgs.snapper ];
134 systemd.services.snapperd = {
135 description = "DBus interface for snapper";
136 inherit documentation;
139 BusName = "org.opensuse.Snapper";
140 ExecStart = "${pkgs.snapper}/bin/snapperd";
141 CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
142 LockPersonality = true;
143 NoNewPrivileges = false;
144 PrivateNetwork = true;
145 ProtectHostname = true;
146 RestrictAddressFamilies = "AF_UNIX";
147 RestrictRealtime = true;
151 systemd.services.snapper-timeline = {
152 description = "Timeline of Snapper Snapshots";
153 inherit documentation;
154 requires = [ "local-fs.target" ];
155 serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
156 startAt = cfg.snapshotInterval;
159 systemd.services.snapper-cleanup = {
160 description = "Cleanup of Snapper Snapshots";
161 inherit documentation;
162 serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
165 systemd.timers.snapper-cleanup = {
166 description = "Cleanup of Snapper Snapshots";
167 inherit documentation;
168 wantedBy = [ "timers.target" ];
169 requires = [ "local-fs.target" ];
170 timerConfig.OnBootSec = "10m";
171 timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
174 systemd.services.snapper-boot = lib.optionalAttrs cfg.snapshotRootOnBoot {
175 description = "Take snapper snapshot of root on boot";
176 inherit documentation;
177 serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
178 serviceConfig.type = "oneshot";
179 requires = [ "local-fs.target" ];
180 wantedBy = [ "multi-user.target" ];
181 unitConfig.ConditionPathExists = "/etc/snapper/configs/root";