1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.rabbitmq;
7
8 inherit (builtins) concatStringsSep;
9
10 config_file_content = lib.generators.toKeyValue { } cfg.configItems;
11 config_file = pkgs.writeText "rabbitmq.conf" config_file_content;
12
13 advanced_config_file = pkgs.writeText "advanced.config" cfg.config;
14
15in
16{
17 ###### interface
18 options = {
19 services.rabbitmq = {
20 enable = mkOption {
21 type = types.bool;
22 default = false;
23 description = lib.mdDoc ''
24 Whether to enable the RabbitMQ server, an Advanced Message
25 Queuing Protocol (AMQP) broker.
26 '';
27 };
28
29 package = mkOption {
30 default = pkgs.rabbitmq-server;
31 type = types.package;
32 defaultText = literalExpression "pkgs.rabbitmq-server";
33 description = lib.mdDoc ''
34 Which rabbitmq package to use.
35 '';
36 };
37
38 listenAddress = mkOption {
39 default = "127.0.0.1";
40 example = "";
41 description = lib.mdDoc ''
42 IP address on which RabbitMQ will listen for AMQP
43 connections. Set to the empty string to listen on all
44 interfaces. Note that RabbitMQ creates a user named
45 `guest` with password
46 `guest` by default, so you should delete
47 this user if you intend to allow external access.
48
49 Together with 'port' setting it's mostly an alias for
50 configItems."listeners.tcp.1" and it's left for backwards
51 compatibility with previous version of this module.
52 '';
53 type = types.str;
54 };
55
56 port = mkOption {
57 default = 5672;
58 description = lib.mdDoc ''
59 Port on which RabbitMQ will listen for AMQP connections.
60 '';
61 type = types.port;
62 };
63
64 dataDir = mkOption {
65 type = types.path;
66 default = "/var/lib/rabbitmq";
67 description = lib.mdDoc ''
68 Data directory for rabbitmq.
69 '';
70 };
71
72 cookie = mkOption {
73 default = "";
74 type = types.str;
75 description = lib.mdDoc ''
76 Erlang cookie is a string of arbitrary length which must
77 be the same for several nodes to be allowed to communicate.
78 Leave empty to generate automatically.
79 '';
80 };
81
82 configItems = mkOption {
83 default = { };
84 type = types.attrsOf types.str;
85 example = literalExpression ''
86 {
87 "auth_backends.1.authn" = "rabbit_auth_backend_ldap";
88 "auth_backends.1.authz" = "rabbit_auth_backend_internal";
89 }
90 '';
91 description = lib.mdDoc ''
92 Configuration options in RabbitMQ's new config file format,
93 which is a simple key-value format that can not express nested
94 data structures. This is known as the `rabbitmq.conf` file,
95 although outside NixOS that filename may have Erlang syntax, particularly
96 prior to RabbitMQ 3.7.0.
97
98 If you do need to express nested data structures, you can use
99 `config` option. Configuration from `config`
100 will be merged into these options by RabbitMQ at runtime to
101 form the final configuration.
102
103 See https://www.rabbitmq.com/configure.html#config-items
104 For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats
105 '';
106 };
107
108 config = mkOption {
109 default = "";
110 type = types.str;
111 description = lib.mdDoc ''
112 Verbatim advanced configuration file contents using the Erlang syntax.
113 This is also known as the `advanced.config` file or the old config format.
114
115 `configItems` is preferred whenever possible. However, nested
116 data structures can only be expressed properly using the `config` option.
117
118 The contents of this option will be merged into the `configItems`
119 by RabbitMQ at runtime to form the final configuration.
120
121 See the second table on https://www.rabbitmq.com/configure.html#config-items
122 For the distinct formats, see https://www.rabbitmq.com/configure.html#config-file-formats
123 '';
124 };
125
126 plugins = mkOption {
127 default = [ ];
128 type = types.listOf types.str;
129 description = lib.mdDoc "The names of plugins to enable";
130 };
131
132 pluginDirs = mkOption {
133 default = [ ];
134 type = types.listOf types.path;
135 description = lib.mdDoc "The list of directories containing external plugins";
136 };
137
138 managementPlugin = {
139 enable = mkEnableOption (lib.mdDoc "the management plugin");
140 port = mkOption {
141 default = 15672;
142 type = types.port;
143 description = lib.mdDoc ''
144 On which port to run the management plugin
145 '';
146 };
147 };
148 };
149 };
150
151
152 ###### implementation
153 config = mkIf cfg.enable {
154
155 # This is needed so we will have 'rabbitmqctl' in our PATH
156 environment.systemPackages = [ cfg.package ];
157
158 services.epmd.enable = true;
159
160 users.users.rabbitmq = {
161 description = "RabbitMQ server user";
162 home = "${cfg.dataDir}";
163 createHome = true;
164 group = "rabbitmq";
165 uid = config.ids.uids.rabbitmq;
166 };
167
168 users.groups.rabbitmq.gid = config.ids.gids.rabbitmq;
169
170 services.rabbitmq.configItems = {
171 "listeners.tcp.1" = mkDefault "${cfg.listenAddress}:${toString cfg.port}";
172 } // optionalAttrs cfg.managementPlugin.enable {
173 "management.tcp.port" = toString cfg.managementPlugin.port;
174 "management.tcp.ip" = cfg.listenAddress;
175 };
176
177 services.rabbitmq.plugins = optional cfg.managementPlugin.enable "rabbitmq_management";
178
179 systemd.services.rabbitmq = {
180 description = "RabbitMQ Server";
181
182 wantedBy = [ "multi-user.target" ];
183 after = [ "network.target" "epmd.socket" ];
184 wants = [ "network.target" "epmd.socket" ];
185
186 path = [
187 cfg.package
188 pkgs.coreutils # mkdir/chown/chmod for preStart
189 ];
190
191 environment = {
192 RABBITMQ_MNESIA_BASE = "${cfg.dataDir}/mnesia";
193 RABBITMQ_LOGS = "-";
194 SYS_PREFIX = "";
195 RABBITMQ_CONFIG_FILE = config_file;
196 RABBITMQ_PLUGINS_DIR = concatStringsSep ":" cfg.pluginDirs;
197 RABBITMQ_ENABLED_PLUGINS_FILE = pkgs.writeText "enabled_plugins" ''
198 [ ${concatStringsSep "," cfg.plugins} ].
199 '';
200 } // optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
201
202 serviceConfig = {
203 ExecStart = "${cfg.package}/sbin/rabbitmq-server";
204 ExecStop = "${cfg.package}/sbin/rabbitmqctl shutdown";
205 User = "rabbitmq";
206 Group = "rabbitmq";
207 LogsDirectory = "rabbitmq";
208 WorkingDirectory = cfg.dataDir;
209 Type = "notify";
210 NotifyAccess = "all";
211 UMask = "0027";
212 LimitNOFILE = "100000";
213 Restart = "on-failure";
214 RestartSec = "10";
215 TimeoutStartSec = "3600";
216 };
217
218 preStart = ''
219 ${optionalString (cfg.cookie != "") ''
220 echo -n ${cfg.cookie} > ${cfg.dataDir}/.erlang.cookie
221 chmod 600 ${cfg.dataDir}/.erlang.cookie
222 ''}
223 '';
224 };
225
226 };
227
228}