1{
2 config,
3 options,
4 lib,
5 pkgs,
6 ...
7}:
8let
9 cfg = config.services.couchdb;
10 opt = options.services.couchdb;
11
12 baseConfig = {
13 couchdb = {
14 database_dir = cfg.databaseDir;
15 uri_file = cfg.uriFile;
16 view_index_dir = cfg.viewIndexDir;
17 };
18 chttpd = {
19 port = cfg.port;
20 bind_address = cfg.bindAddress;
21 };
22 log = {
23 file = cfg.logFile;
24 };
25 };
26 adminConfig = lib.optionalAttrs (cfg.adminPass != null) {
27 admins = {
28 "${cfg.adminUser}" = cfg.adminPass;
29 };
30 };
31 appConfig = lib.recursiveUpdate (lib.recursiveUpdate baseConfig adminConfig) cfg.extraConfig;
32
33 optionsConfigFile = pkgs.writeText "couchdb.ini" (lib.generators.toINI { } appConfig);
34
35 # we are actually specifying 5 configuration files:
36 # 1. the preinstalled default.ini
37 # 2. the module configuration
38 # 3. the extraConfigFiles from the module options
39 # 4. the locally writable config file, which couchdb itself writes to
40 configFiles =
41 [
42 "${cfg.package}/etc/default.ini"
43 optionsConfigFile
44 ]
45 ++ cfg.extraConfigFiles
46 ++ [ cfg.configFile ];
47 executable = "${cfg.package}/bin/couchdb";
48in
49{
50 ###### interface
51
52 options = {
53 services.couchdb = {
54 enable = lib.mkEnableOption "CouchDB Server";
55
56 package = lib.mkPackageOption pkgs "couchdb3" { };
57
58 adminUser = lib.mkOption {
59 type = lib.types.str;
60 default = "admin";
61 description = ''
62 Couchdb (i.e. fauxton) account with permission for all dbs and
63 tasks.
64 '';
65 };
66
67 adminPass = lib.mkOption {
68 type = lib.types.nullOr lib.types.str;
69 default = null;
70 description = ''
71 Couchdb (i.e. fauxton) account with permission for all dbs and
72 tasks.
73 '';
74 };
75
76 user = lib.mkOption {
77 type = lib.types.str;
78 default = "couchdb";
79 description = ''
80 User account under which couchdb runs.
81 '';
82 };
83
84 group = lib.mkOption {
85 type = lib.types.str;
86 default = "couchdb";
87 description = ''
88 Group account under which couchdb runs.
89 '';
90 };
91
92 # couchdb options: https://docs.couchdb.org/en/latest/config/index.html
93
94 databaseDir = lib.mkOption {
95 type = lib.types.path;
96 default = "/var/lib/couchdb";
97 description = ''
98 Specifies location of CouchDB database files (*.couch named). This
99 location should be writable and readable for the user the CouchDB
100 service runs as (couchdb by default).
101 '';
102 };
103
104 uriFile = lib.mkOption {
105 type = lib.types.path;
106 default = "/run/couchdb/couchdb.uri";
107 description = ''
108 This file contains the full URI that can be used to access this
109 instance of CouchDB. It is used to help discover the port CouchDB is
110 running on (if it was set to 0 (e.g. automatically assigned any free
111 one). This file should be writable and readable for the user that
112 runs the CouchDB service (couchdb by default).
113 '';
114 };
115
116 viewIndexDir = lib.mkOption {
117 type = lib.types.path;
118 default = "/var/lib/couchdb";
119 description = ''
120 Specifies location of CouchDB view index files. This location should
121 be writable and readable for the user that runs the CouchDB service
122 (couchdb by default).
123 '';
124 };
125
126 bindAddress = lib.mkOption {
127 type = lib.types.str;
128 default = "127.0.0.1";
129 description = ''
130 Defines the IP address by which CouchDB will be accessible.
131 '';
132 };
133
134 port = lib.mkOption {
135 type = lib.types.port;
136 default = 5984;
137 description = ''
138 Defined the port number to listen.
139 '';
140 };
141
142 logFile = lib.mkOption {
143 type = lib.types.path;
144 default = "/var/log/couchdb.log";
145 description = ''
146 Specifies the location of file for logging output.
147 '';
148 };
149
150 extraConfig = lib.mkOption {
151 type = lib.types.attrs;
152 default = { };
153 description = "Extra configuration options for CouchDB";
154 };
155 extraConfigFiles = lib.mkOption {
156 type = lib.types.listOf lib.types.path;
157 default = [ ];
158 description = ''
159 Extra configuration files. Overrides any other configuration. You can use this to setup the Admin user without putting the password in your nix store.
160 '';
161 };
162
163 argsFile = lib.mkOption {
164 type = lib.types.path;
165 default = "${cfg.package}/etc/vm.args";
166 defaultText = lib.literalExpression ''"config.${opt.package}/etc/vm.args"'';
167 description = ''
168 vm.args configuration. Overrides Couchdb's Erlang VM parameters file.
169 '';
170 };
171
172 configFile = lib.mkOption {
173 type = lib.types.path;
174 default = "/var/lib/couchdb/local.ini";
175 description = ''
176 Configuration file for persisting runtime changes. File
177 needs to be readable and writable from couchdb user/group.
178 '';
179 };
180 };
181 };
182
183 ###### implementation
184
185 config = lib.mkIf cfg.enable {
186 environment.systemPackages = [ cfg.package ];
187
188 systemd.tmpfiles.rules = [
189 "d '${dirOf cfg.uriFile}' - ${cfg.user} ${cfg.group} - -"
190 "f '${cfg.logFile}' - ${cfg.user} ${cfg.group} - -"
191 "d '${cfg.databaseDir}' - ${cfg.user} ${cfg.group} - -"
192 "d '${cfg.viewIndexDir}' - ${cfg.user} ${cfg.group} - -"
193 ];
194
195 systemd.services.couchdb = {
196 description = "CouchDB Server";
197 wantedBy = [ "multi-user.target" ];
198
199 preStart = ''
200 touch ${cfg.configFile}
201 if ! test -e ${cfg.databaseDir}/.erlang.cookie; then
202 touch ${cfg.databaseDir}/.erlang.cookie
203 chmod 600 ${cfg.databaseDir}/.erlang.cookie
204 dd if=/dev/random bs=16 count=1 | base64 > ${cfg.databaseDir}/.erlang.cookie
205 fi
206 '';
207
208 environment = {
209 ERL_FLAGS = ''-couch_ini ${lib.concatStringsSep " " configFiles}'';
210 # 5. the vm.args file
211 COUCHDB_ARGS_FILE = ''${cfg.argsFile}'';
212 HOME = ''${cfg.databaseDir}'';
213 };
214
215 serviceConfig = {
216 User = cfg.user;
217 Group = cfg.group;
218 ExecStart = executable;
219 };
220 };
221
222 users.users.couchdb = {
223 description = "CouchDB Server user";
224 group = "couchdb";
225 uid = config.ids.uids.couchdb;
226 };
227
228 users.groups.couchdb.gid = config.ids.gids.couchdb;
229 };
230}