1 { config, lib, pkgs, ... }:
3 cfg = config.services.foundationdb;
6 # used for initial cluster configuration
7 initialIpAddr = if (cfg.publicAddress != "auto") then cfg.publicAddress else "127.0.0.1";
10 lib.concatStringsSep "\n" (map (x: "[fdbserver.${toString (x+cfg.listenPortStart)}]") (lib.range 0 (n - 1)));
13 lib.concatStringsSep "\n" (map (x: "[backup_agent.${toString x}]") (lib.range 1 n));
15 configFile = pkgs.writeText "foundationdb.conf" ''
17 cluster_file = /etc/foundationdb/fdb.cluster
20 restart_delay = ${toString cfg.restartDelay}
25 command = ${pkg}/bin/fdbserver
26 public_address = ${cfg.publicAddress}:$ID
27 listen_address = ${cfg.listenAddress}
28 datadir = ${cfg.dataDir}/$ID
29 logdir = ${cfg.logDir}
30 logsize = ${cfg.logSize}
31 maxlogssize = ${cfg.maxLogSize}
32 ${lib.optionalString (cfg.class != null) "class = ${cfg.class}"}
33 memory = ${cfg.memory}
34 storage_memory = ${cfg.storageMemory}
36 ${lib.optionalString (lib.versionAtLeast cfg.package.version "6.1") ''
37 trace_format = ${cfg.traceFormat}
40 ${lib.optionalString (cfg.tls != null) ''
41 tls_plugin = ${pkg}/libexec/plugins/FDBLibTLS.so
42 tls_certificate_file = ${cfg.tls.certificate}
43 tls_key_file = ${cfg.tls.key}
44 tls_verify_peers = ${cfg.tls.allowedPeers}
47 ${lib.optionalString (cfg.locality.machineId != null) "locality_machineid=${cfg.locality.machineId}"}
48 ${lib.optionalString (cfg.locality.zoneId != null) "locality_zoneid=${cfg.locality.zoneId}"}
49 ${lib.optionalString (cfg.locality.datacenterId != null) "locality_dcid=${cfg.locality.datacenterId}"}
50 ${lib.optionalString (cfg.locality.dataHall != null) "locality_data_hall=${cfg.locality.dataHall}"}
52 ${fdbServers cfg.serverProcesses}
55 command = ${pkg}/libexec/backup_agent
56 ${backupAgents cfg.backupProcesses}
60 options.services.foundationdb = {
62 enable = lib.mkEnableOption "FoundationDB Server";
64 package = lib.mkOption {
65 type = lib.types.package;
67 The FoundationDB package to use for this server. This must be specified by the user
68 in order to ensure migrations and upgrades are controlled appropriately.
72 publicAddress = lib.mkOption {
75 description = "Publicly visible IP address of the process. Port is determined by process ID";
78 listenAddress = lib.mkOption {
81 description = "Publicly visible IP address of the process. Port is determined by process ID";
84 listenPortStart = lib.mkOption {
88 Starting port number for database listening sockets. Every FDB process binds to a
89 subsequent port, to this number reflects the start of the overall range. e.g. having
90 8 server processes will use all ports between 4500 and 4507.
94 openFirewall = lib.mkOption {
95 type = lib.types.bool;
98 Open the firewall ports corresponding to FoundationDB processes and coordinators
99 using {option}`config.networking.firewall.*`.
103 dataDir = lib.mkOption {
104 type = lib.types.path;
105 default = "/var/lib/foundationdb";
106 description = "Data directory. All cluster data will be put under here.";
109 logDir = lib.mkOption {
110 type = lib.types.path;
111 default = "/var/log/foundationdb";
112 description = "Log directory.";
115 user = lib.mkOption {
116 type = lib.types.str;
117 default = "foundationdb";
118 description = "User account under which FoundationDB runs.";
121 group = lib.mkOption {
122 type = lib.types.str;
123 default = "foundationdb";
124 description = "Group account under which FoundationDB runs.";
127 class = lib.mkOption {
128 type = lib.types.nullOr (lib.types.enum [ "storage" "transaction" "stateless" ]);
130 description = "Process class";
133 restartDelay = lib.mkOption {
134 type = lib.types.int;
136 description = "Number of seconds to wait before restarting servers.";
139 logSize = lib.mkOption {
140 type = lib.types.str;
143 Roll over to a new log file after the current log file
144 reaches the specified size.
148 maxLogSize = lib.mkOption {
149 type = lib.types.str;
152 Delete the oldest log file when the total size of all log
153 files exceeds the specified size. If set to 0, old log files
158 serverProcesses = lib.mkOption {
159 type = lib.types.int;
161 description = "Number of fdbserver processes to run.";
164 backupProcesses = lib.mkOption {
165 type = lib.types.int;
167 description = "Number of backup_agent processes to run for snapshots.";
170 memory = lib.mkOption {
171 type = lib.types.str;
174 Maximum memory used by the process. The default value is
175 `8GiB`. When specified without a unit,
176 `MiB` is assumed. This parameter does not
177 change the memory allocation of the program. Rather, it sets
178 a hard limit beyond which the process will kill itself and
179 be restarted. The default value of `8GiB`
180 is double the intended memory usage in the default
181 configuration (providing an emergency buffer to deal with
182 memory leaks or similar problems). It is not recommended to
183 decrease the value of this parameter below its default
184 value. It may be increased if you wish to allocate a very
185 large amount of storage engine memory or cache. In
186 particular, when the `storageMemory`
187 parameter is increased, the `memory`
188 parameter should be increased by an equal amount.
192 storageMemory = lib.mkOption {
193 type = lib.types.str;
196 Maximum memory used for data storage. The default value is
197 `1GiB`. When specified without a unit,
198 `MB` is assumed. Clusters using the memory
199 storage engine will be restricted to using this amount of
200 memory per process for purposes of data storage. Memory
201 overhead associated with storing the data is counted against
202 this total. If you increase the
203 `storageMemory`, you should also increase
204 the `memory` parameter by the same amount.
211 FoundationDB Transport Security Layer (TLS) settings.
214 type = lib.types.nullOr (lib.types.submodule ({
216 certificate = lib.mkOption {
217 type = lib.types.str;
219 Path to the TLS certificate file. This certificate will
220 be offered to, and may be verified by, clients.
225 type = lib.types.str;
226 description = "Private key file for the certificate.";
229 allowedPeers = lib.mkOption {
230 type = lib.types.str;
231 default = "Check.Valid=1,Check.Unexpired=1";
233 "Peer verification string". This may be used to adjust which TLS
234 client certificates a server will accept, as a form of user
235 authorization; for example, it may only accept TLS clients who
236 offer a certificate abiding by some locality or organization name.
238 For more information, please see the FoundationDB documentation.
245 locality = lib.mkOption {
254 FoundationDB locality settings.
257 type = lib.types.submodule ({
259 machineId = lib.mkOption {
261 type = lib.types.nullOr lib.types.str;
263 Machine identifier key. All processes on a machine should share a
264 unique id. By default, processes on a machine determine a unique id to share.
265 This does not generally need to be set.
269 zoneId = lib.mkOption {
271 type = lib.types.nullOr lib.types.str;
273 Zone identifier key. Processes that share a zone id are
274 considered non-unique for the purposes of data replication.
275 If unset, defaults to machine id.
279 datacenterId = lib.mkOption {
281 type = lib.types.nullOr lib.types.str;
283 Data center identifier key. All processes physically located in a
284 data center should share the id. If you are depending on data
285 center based replication this must be set on all processes.
289 dataHall = lib.mkOption {
291 type = lib.types.nullOr lib.types.str;
293 Data hall identifier key. All processes physically located in a
294 data hall should share the id. If you are depending on data
295 hall based replication this must be set on all processes.
302 extraReadWritePaths = lib.mkOption {
304 type = lib.types.listOf lib.types.path;
306 An extra set of filesystem paths that FoundationDB can read to
307 and write from. By default, FoundationDB runs under a heavily
308 namespaced systemd environment without write access to most of
309 the filesystem outside of its data and log directories. By
310 adding paths to this list, the set of writeable paths will be
311 expanded. This is useful for allowing e.g. backups to local files,
312 which must be performed on behalf of the foundationdb service.
316 pidfile = lib.mkOption {
317 type = lib.types.path;
318 default = "/run/foundationdb.pid";
319 description = "Path to pidfile for fdbmonitor.";
322 traceFormat = lib.mkOption {
323 type = lib.types.enum [ "xml" "json" ];
325 description = "Trace logging format.";
329 config = lib.mkIf cfg.enable {
331 { assertion = lib.versionOlder cfg.package.version "6.1" -> cfg.traceFormat == "xml";
333 Versions of FoundationDB before 6.1 do not support configurable trace formats (only XML is supported).
334 This option has no effect for version '' + cfg.package.version + '', and enabling it is an error.
339 environment.systemPackages = [ pkg ];
341 users.users = lib.optionalAttrs (cfg.user == "foundationdb") {
343 description = "FoundationDB User";
344 uid = config.ids.uids.foundationdb;
349 users.groups = lib.optionalAttrs (cfg.group == "foundationdb") {
350 foundationdb.gid = config.ids.gids.foundationdb;
353 networking.firewall.allowedTCPPortRanges = lib.mkIf cfg.openFirewall
354 [ { from = cfg.listenPortStart;
355 to = (cfg.listenPortStart + cfg.serverProcesses) - 1;
359 systemd.tmpfiles.rules = [
360 "d /etc/foundationdb 0755 ${cfg.user} ${cfg.group} - -"
361 "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
362 "d '${cfg.logDir}' 0770 ${cfg.user} ${cfg.group} - -"
363 "F '${cfg.pidfile}' - ${cfg.user} ${cfg.group} - -"
366 systemd.services.foundationdb = {
367 description = "FoundationDB Service";
369 after = [ "network.target" ];
370 wantedBy = [ "multi-user.target" ];
372 { RequiresMountsFor = "${cfg.dataDir} ${cfg.logDir}";
376 let rwpaths = [ cfg.dataDir cfg.logDir cfg.pidfile "/etc/foundationdb" ]
377 ++ cfg.extraReadWritePaths;
384 PIDFile = "${cfg.pidfile}";
386 PermissionsStartOnly = true; # setup needs root perms
387 TimeoutSec = 120; # give reasonable time to shut down
390 NoNewPrivileges = true;
392 ProtectSystem = "strict";
393 ProtectKernelTunables = true;
394 ProtectControlGroups = true;
396 PrivateDevices = true;
397 ReadWritePaths = lib.concatStringsSep " " (map (x: "-" + x) rwpaths);
400 path = [ pkg pkgs.coreutils ];
403 if [ ! -f /etc/foundationdb/fdb.cluster ]; then
404 cf=/etc/foundationdb/fdb.cluster
405 desc=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8)
406 rand=$(tr -dc A-Za-z0-9 </dev/urandom 2>/dev/null | head -c8)
407 echo ''${desc}:''${rand}@${initialIpAddr}:${builtins.toString cfg.listenPortStart} > $cf
409 touch "${cfg.dataDir}/.first_startup"
413 script = "exec fdbmonitor --lockfile ${cfg.pidfile} --conffile ${configFile}";
416 if [ -e "${cfg.dataDir}/.first_startup" ]; then
417 fdbcli --exec "configure new single ssd"
418 rm -f "${cfg.dataDir}/.first_startup";
424 meta.doc = ./foundationdb.md;
425 meta.maintainers = with lib.maintainers; [ thoughtpolice ];