1{
2 config,
3 lib,
4 pkgs,
5 utils,
6 ...
7}:
8let
9 cfg = config.services.cockroachdb;
10 crdb = cfg.package;
11
12 startupCommand = utils.escapeSystemdExecArgs (
13 [
14 # Basic startup
15 "${crdb}/bin/cockroach"
16 "start"
17 "--logtostderr"
18 "--store=/var/lib/cockroachdb"
19
20 # WebUI settings
21 "--http-addr=${cfg.http.address}:${toString cfg.http.port}"
22
23 # Cluster listen address
24 "--listen-addr=${cfg.listen.address}:${toString cfg.listen.port}"
25
26 # Cache and memory settings.
27 "--cache=${cfg.cache}"
28 "--max-sql-memory=${cfg.maxSqlMemory}"
29
30 # Certificate/security settings.
31 (if cfg.insecure then "--insecure" else "--certs-dir=${cfg.certsDir}")
32 ]
33 ++ lib.optional (cfg.join != null) "--join=${cfg.join}"
34 ++ lib.optional (cfg.locality != null) "--locality=${cfg.locality}"
35 ++ cfg.extraArgs
36 );
37
38 addressOption = descr: defaultPort: {
39 address = lib.mkOption {
40 type = lib.types.str;
41 default = "localhost";
42 description = "Address to bind to for ${descr}";
43 };
44
45 port = lib.mkOption {
46 type = lib.types.port;
47 default = defaultPort;
48 description = "Port to bind to for ${descr}";
49 };
50 };
51in
52
53{
54 options = {
55 services.cockroachdb = {
56 enable = lib.mkEnableOption "CockroachDB Server";
57
58 listen = addressOption "intra-cluster communication" 26257;
59
60 http = addressOption "http-based Admin UI" 8080;
61
62 locality = lib.mkOption {
63 type = lib.types.nullOr lib.types.str;
64 default = null;
65 description = ''
66 An ordered, comma-separated list of key-value pairs that describe the
67 topography of the machine. Topography might include country,
68 datacenter or rack designations. Data is automatically replicated to
69 maximize diversities of each tier. The order of tiers is used to
70 determine the priority of the diversity, so the more inclusive
71 localities like country should come before less inclusive localities
72 like datacenter. The tiers and order must be the same on all nodes.
73 Including more tiers is better than including fewer. For example:
74
75 ```
76 country=us,region=us-west,datacenter=us-west-1b,rack=12
77 country=ca,region=ca-east,datacenter=ca-east-2,rack=4
78
79 planet=earth,province=manitoba,colo=secondary,power=3
80 ```
81 '';
82 };
83
84 join = lib.mkOption {
85 type = lib.types.nullOr lib.types.str;
86 default = null;
87 description = "The addresses for connecting the node to a cluster.";
88 };
89
90 insecure = lib.mkOption {
91 type = lib.types.bool;
92 default = false;
93 description = "Run in insecure mode.";
94 };
95
96 certsDir = lib.mkOption {
97 type = lib.types.nullOr lib.types.path;
98 default = null;
99 description = "The path to the certificate directory.";
100 };
101
102 user = lib.mkOption {
103 type = lib.types.str;
104 default = "cockroachdb";
105 description = "User account under which CockroachDB runs";
106 };
107
108 group = lib.mkOption {
109 type = lib.types.str;
110 default = "cockroachdb";
111 description = "User account under which CockroachDB runs";
112 };
113
114 openPorts = lib.mkOption {
115 type = lib.types.bool;
116 default = false;
117 description = "Open firewall ports for cluster communication by default";
118 };
119
120 cache = lib.mkOption {
121 type = lib.types.str;
122 default = "25%";
123 description = ''
124 The total size for caches.
125
126 This can be a percentage, expressed with a fraction sign or as a
127 decimal-point number, or any bytes-based unit. For example,
128 `"25%"`, `"0.25"` both represent
129 25% of the available system memory. The values
130 `"1000000000"` and `"1GB"` both
131 represent 1 gigabyte of memory.
132
133 '';
134 };
135
136 maxSqlMemory = lib.mkOption {
137 type = lib.types.str;
138 default = "25%";
139 description = ''
140 The maximum in-memory storage capacity available to store temporary
141 data for SQL queries.
142
143 This can be a percentage, expressed with a fraction sign or as a
144 decimal-point number, or any bytes-based unit. For example,
145 `"25%"`, `"0.25"` both represent
146 25% of the available system memory. The values
147 `"1000000000"` and `"1GB"` both
148 represent 1 gigabyte of memory.
149 '';
150 };
151
152 package = lib.mkPackageOption pkgs "cockroachdb" {
153 extraDescription = ''
154 This would primarily be useful to enable Enterprise Edition features
155 in your own custom CockroachDB build (Nixpkgs CockroachDB binaries
156 only contain open source features and open source code).
157 '';
158 };
159
160 extraArgs = lib.mkOption {
161 type = lib.types.listOf lib.types.str;
162 default = [ ];
163 example = [
164 "--advertise-addr"
165 "[fe80::f6f2:::]"
166 ];
167 description = ''
168 Extra CLI arguments passed to {command}`cockroach start`.
169 For the full list of supported arguments, check <https://www.cockroachlabs.com/docs/stable/cockroach-start.html#flags>
170 '';
171 };
172 };
173 };
174
175 config = lib.mkIf config.services.cockroachdb.enable {
176 assertions = [
177 {
178 assertion = !cfg.insecure -> cfg.certsDir != null;
179 message = "CockroachDB must have a set of SSL certificates (.certsDir), or run in Insecure Mode (.insecure = true)";
180 }
181 ];
182
183 environment.systemPackages = [ crdb ];
184
185 users.users = lib.optionalAttrs (cfg.user == "cockroachdb") {
186 cockroachdb = {
187 description = "CockroachDB Server User";
188 uid = config.ids.uids.cockroachdb;
189 group = cfg.group;
190 };
191 };
192
193 users.groups = lib.optionalAttrs (cfg.group == "cockroachdb") {
194 cockroachdb.gid = config.ids.gids.cockroachdb;
195 };
196
197 networking.firewall.allowedTCPPorts = lib.optionals cfg.openPorts [
198 cfg.http.port
199 cfg.listen.port
200 ];
201
202 systemd.services.cockroachdb = {
203 description = "CockroachDB Server";
204 documentation = [
205 "man:cockroach(1)"
206 "https://www.cockroachlabs.com"
207 ];
208
209 after = [
210 "network.target"
211 "time-sync.target"
212 ];
213 requires = [ "time-sync.target" ];
214 wantedBy = [ "multi-user.target" ];
215
216 unitConfig.RequiresMountsFor = "/var/lib/cockroachdb";
217
218 serviceConfig = {
219 ExecStart = startupCommand;
220 Type = "notify";
221 User = cfg.user;
222 StateDirectory = "cockroachdb";
223 StateDirectoryMode = "0700";
224
225 Restart = "always";
226
227 # A conservative-ish timeout is alright here, because for Type=notify
228 # cockroach will send systemd pings during startup to keep it alive
229 TimeoutStopSec = 60;
230 RestartSec = 10;
231 };
232 };
233 };
234
235 meta.maintainers = with lib.maintainers; [ thoughtpolice ];
236}