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