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