1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.dragonflydb;
9 dragonflydb = pkgs.dragonflydb;
10
11 settings =
12 {
13 port = cfg.port;
14 dir = "/var/lib/dragonflydb";
15 keys_output_limit = cfg.keysOutputLimit;
16 }
17 // (lib.optionalAttrs (cfg.bind != null) { bind = cfg.bind; })
18 // (lib.optionalAttrs (cfg.requirePass != null) { requirepass = cfg.requirePass; })
19 // (lib.optionalAttrs (cfg.maxMemory != null) { maxmemory = cfg.maxMemory; })
20 // (lib.optionalAttrs (cfg.memcachePort != null) { memcache_port = cfg.memcachePort; })
21 // (lib.optionalAttrs (cfg.dbNum != null) { dbnum = cfg.dbNum; })
22 // (lib.optionalAttrs (cfg.cacheMode != null) { cache_mode = cfg.cacheMode; });
23in
24{
25
26 ###### interface
27
28 options = {
29 services.dragonflydb = {
30 enable = lib.mkEnableOption "DragonflyDB";
31
32 user = lib.mkOption {
33 type = lib.types.str;
34 default = "dragonfly";
35 description = "The user to run DragonflyDB as";
36 };
37
38 port = lib.mkOption {
39 type = lib.types.port;
40 default = 6379;
41 description = "The TCP port to accept connections.";
42 };
43
44 bind = lib.mkOption {
45 type = with lib.types; nullOr str;
46 default = "127.0.0.1";
47 description = ''
48 The IP interface to bind to.
49 `null` means "all interfaces".
50 '';
51 };
52
53 requirePass = lib.mkOption {
54 type = with lib.types; nullOr str;
55 default = null;
56 description = "Password for database";
57 example = "letmein!";
58 };
59
60 maxMemory = lib.mkOption {
61 type = with lib.types; nullOr ints.unsigned;
62 default = null;
63 description = ''
64 The maximum amount of memory to use for storage (in bytes).
65 `null` means this will be automatically set.
66 '';
67 };
68
69 memcachePort = lib.mkOption {
70 type = with lib.types; nullOr port;
71 default = null;
72 description = ''
73 To enable memcached compatible API on this port.
74 `null` means disabled.
75 '';
76 };
77
78 keysOutputLimit = lib.mkOption {
79 type = lib.types.ints.unsigned;
80 default = 8192;
81 description = ''
82 Maximum number of returned keys in keys command.
83 `keys` is a dangerous command.
84 We truncate its result to avoid blowup in memory when fetching too many keys.
85 '';
86 };
87
88 dbNum = lib.mkOption {
89 type = with lib.types; nullOr ints.unsigned;
90 default = null;
91 description = "Maximum number of supported databases for `select`";
92 };
93
94 cacheMode = lib.mkOption {
95 type = with lib.types; nullOr bool;
96 default = null;
97 description = ''
98 Once this mode is on, Dragonfly will evict items least likely to be stumbled
99 upon in the future but only when it is near maxmemory limit.
100 '';
101 };
102 };
103 };
104
105 ###### implementation
106
107 config = lib.mkIf config.services.dragonflydb.enable {
108
109 users.users = lib.optionalAttrs (cfg.user == "dragonfly") {
110 dragonfly.description = "DragonflyDB server user";
111 dragonfly.isSystemUser = true;
112 dragonfly.group = "dragonfly";
113 };
114 users.groups = lib.optionalAttrs (cfg.user == "dragonfly") { dragonfly = { }; };
115
116 environment.systemPackages = [ dragonflydb ];
117
118 systemd.services.dragonflydb = {
119 description = "DragonflyDB server";
120
121 wantedBy = [ "multi-user.target" ];
122 after = [ "network.target" ];
123
124 serviceConfig = {
125 ExecStart = "${dragonflydb}/bin/dragonfly --alsologtostderr ${
126 lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "--${n} ${lib.escapeShellArg v}") settings)
127 }";
128
129 User = cfg.user;
130
131 # Filesystem access
132 ReadWritePaths = [ settings.dir ];
133 StateDirectory = "dragonflydb";
134 StateDirectoryMode = "0700";
135 # Process Properties
136 LimitMEMLOCK = "infinity";
137 # Caps
138 CapabilityBoundingSet = "";
139 NoNewPrivileges = true;
140 # Sandboxing
141 ProtectSystem = "strict";
142 ProtectHome = true;
143 PrivateTmp = true;
144 PrivateDevices = true;
145 ProtectKernelTunables = true;
146 ProtectKernelModules = true;
147 ProtectControlGroups = true;
148 LockPersonality = true;
149 RestrictAddressFamilies = [
150 "AF_INET"
151 "AF_INET6"
152 ];
153 RestrictRealtime = true;
154 PrivateMounts = true;
155 MemoryDenyWriteExecute = true;
156 };
157 };
158 };
159}