diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index ae5084ca2a2..5074976fafa 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -336,6 +336,7 @@
./services/misc/apache-kafka.nix
./services/misc/autofs.nix
./services/misc/autorandr.nix
+ ./services/misc/bees.nix
./services/misc/bepasty.nix
./services/misc/canto-daemon.nix
./services/misc/calibre-server.nix
diff --git a/nixos/modules/services/misc/bees.nix b/nixos/modules/services/misc/bees.nix
new file mode 100644
index 00000000000..b0ed2d5c286
--- /dev/null
+++ b/nixos/modules/services/misc/bees.nix
@@ -0,0 +1,123 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.services.beesd;
+
+ logLevels = { emerg = 0; alert = 1; crit = 2; err = 3; warning = 4; notice = 5; info = 6; debug = 7; };
+
+ fsOptions = with types; {
+ options.spec = mkOption {
+ type = str;
+ description = ''
+ Description of how to identify the filesystem to be duplicated by this
+ instance of bees. Note that deduplication crosses subvolumes; one must
+ not configure multiple instances for subvolumes of the same filesystem
+ (or block devices which are part of the same filesystem), but only for
+ completely independent btrfs filesystems.
+
+
+ This must be in a format usable by findmnt; that could be a key=value
+ pair, or a bare path to a mount point.
+ '';
+ example = "LABEL=MyBulkDataDrive";
+ };
+ options.hashTableSizeMB = mkOption {
+ type = types.addCheck types.int (n: mod n 16 == 0);
+ default = 1024; # 1GB; default from upstream beesd script
+ description = ''
+ Hash table size in MB; must be a multiple of 16.
+
+
+ A larger ratio of index size to storage size means smaller blocks of
+ duplicate content are recognized.
+
+
+ If you have 1TB of data, a 4GB hash table (which is to say, a value of
+ 4096) will permit 4KB extents (the smallest possible size) to be
+ recognized, whereas a value of 1024 -- creating a 1GB hash table --
+ will recognize only aligned duplicate blocks of 16KB.
+ '';
+ };
+ options.verbosity = mkOption {
+ type = types.enum (attrNames logLevels ++ attrValues logLevels);
+ apply = v: if isString v then logLevels.${v} else v;
+ default = "info";
+ description = "Log verbosity (syslog keyword/level).";
+ };
+ options.workDir = mkOption {
+ type = str;
+ default = ".beeshome";
+ description = ''
+ Name (relative to the root of the filesystem) of the subvolume where
+ the hash table will be stored.
+ '';
+ };
+ options.extraOptions = mkOption {
+ type = listOf str;
+ default = [];
+ description = ''
+ Extra command-line options passed to the daemon. See upstream bees documentation.
+ '';
+ example = literalExample ''
+ [ "--thread-count" "4" ]
+ '';
+ };
+ };
+
+in {
+
+ options.services.beesd = {
+ filesystems = mkOption {
+ type = with types; attrsOf (submodule fsOptions);
+ description = "BTRFS filesystems to run block-level deduplication on.";
+ default = { };
+ example = literalExample ''
+ {
+ root = {
+ spec = "LABEL=root";
+ hashTableSizeMB = 2048;
+ verbosity = "crit";
+ extraOptions = [ "--loadavg-target" "5.0" ];
+ };
+ }
+ '';
+ };
+ };
+ config = {
+ systemd.services = mapAttrs' (name: fs: nameValuePair "beesd@${name}" {
+ description = "Block-level BTRFS deduplication for %i";
+ after = [ "sysinit.target" ];
+
+ serviceConfig = let
+ configOpts = [
+ fs.spec
+ "verbosity=${toString fs.verbosity}"
+ "idxSizeMB=${toString fs.hashTableSizeMB}"
+ "workDir=${fs.workDir}"
+ ];
+ configOptsStr = escapeShellArgs configOpts;
+ in {
+ # Values from https://github.com/Zygo/bees/blob/v0.6.1/scripts/beesd%40.service.in
+ ExecStart = "${pkgs.bees}/bin/bees-service-wrapper run ${configOptsStr} -- --no-timestamps ${escapeShellArgs fs.extraOptions}";
+ ExecStopPost = "${pkgs.bees}/bin/bees-service-wrapper cleanup ${configOptsStr}";
+ CPUAccounting = true;
+ CPUWeight = 12;
+ IOSchedulingClass = "idle";
+ IOSchedulingPriority = 7;
+ IOWeight = 10;
+ KillMode = "control-group";
+ KillSignal = "SIGTERM";
+ MemoryAccounting = true;
+ Nice = 19;
+ Restart = "on-abnormal";
+ StartupCPUWeight = 25;
+ StartupIOWeight = 25;
+ SyslogIdentifier = "bees"; # would otherwise be "bees-service-wrapper"
+ };
+ wantedBy = ["multi-user.target"];
+ }) cfg.filesystems;
+ };
+}