1<chapter xmlns="http://docbook.org/ns/docbook"
2 xmlns:xlink="http://www.w3.org/1999/xlink"
3 xmlns:xi="http://www.w3.org/2001/XInclude"
4 version="5.0"
5 xml:id="module-services-discourse">
6 <title>Discourse</title>
7 <para>
8 <link xlink:href="https://www.discourse.org/">Discourse</link> is a
9 modern and open source discussion platform.
10 </para>
11
12 <section xml:id="module-services-discourse-basic-usage">
13 <title>Basic usage</title>
14 <para>
15 A minimal configuration using Let's Encrypt for TLS certificates looks like this:
16<programlisting>
17services.discourse = {
18 <link linkend="opt-services.discourse.enable">enable</link> = true;
19 <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
20 admin = {
21 <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
22 <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
23 <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
24 <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
25 };
26 <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
27};
28<link linkend="opt-security.acme.email">security.acme.email</link> = "me@example.com";
29<link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true;
30</programlisting>
31 </para>
32
33 <para>
34 Provided a proper DNS setup, you'll be able to connect to the
35 instance at <literal>discourse.example.com</literal> and log in
36 using the credentials provided in
37 <literal>services.discourse.admin</literal>.
38 </para>
39 </section>
40
41 <section xml:id="module-services-discourse-tls">
42 <title>Using a regular TLS certificate</title>
43 <para>
44 To set up TLS using a regular certificate and key on file, use
45 the <xref linkend="opt-services.discourse.sslCertificate" />
46 and <xref linkend="opt-services.discourse.sslCertificateKey" />
47 options:
48
49<programlisting>
50services.discourse = {
51 <link linkend="opt-services.discourse.enable">enable</link> = true;
52 <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
53 <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
54 <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
55 admin = {
56 <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
57 <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
58 <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
59 <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
60 };
61 <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
62};
63</programlisting>
64
65 </para>
66 </section>
67
68 <section xml:id="module-services-discourse-database">
69 <title>Database access</title>
70 <para>
71 <productname>Discourse</productname> uses
72 <productname>PostgreSQL</productname> to store most of its
73 data. A database will automatically be enabled and a database
74 and role created unless <xref
75 linkend="opt-services.discourse.database.host" /> is changed from
76 its default of <literal>null</literal> or <xref
77 linkend="opt-services.discourse.database.createLocally" /> is set
78 to <literal>false</literal>.
79 </para>
80
81 <para>
82 External database access can also be configured by setting
83 <xref linkend="opt-services.discourse.database.host" />, <xref
84 linkend="opt-services.discourse.database.username" /> and <xref
85 linkend="opt-services.discourse.database.passwordFile" /> as
86 appropriate. Note that you need to manually create a database
87 called <literal>discourse</literal> (or the name you chose in
88 <xref linkend="opt-services.discourse.database.name" />) and
89 allow the configured database user full access to it.
90 </para>
91 </section>
92
93 <section xml:id="module-services-discourse-mail">
94 <title>Email</title>
95 <para>
96 In addition to the basic setup, you'll want to configure an SMTP
97 server <productname>Discourse</productname> can use to send user
98 registration and password reset emails, among others. You can
99 also optionally let <productname>Discourse</productname> receive
100 email, which enables people to reply to threads and conversations
101 via email.
102 </para>
103
104 <para>
105 A basic setup which assumes you want to use your configured <link
106 linkend="opt-services.discourse.hostname">hostname</link> as
107 email domain can be done like this:
108
109<programlisting>
110services.discourse = {
111 <link linkend="opt-services.discourse.enable">enable</link> = true;
112 <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
113 <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
114 <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
115 admin = {
116 <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
117 <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
118 <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
119 <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
120 };
121 mail.outgoing = {
122 <link linkend="opt-services.discourse.mail.outgoing.serverAddress">serverAddress</link> = "smtp.emailprovider.com";
123 <link linkend="opt-services.discourse.mail.outgoing.port">port</link> = 587;
124 <link linkend="opt-services.discourse.mail.outgoing.username">username</link> = "user@emailprovider.com";
125 <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
126 };
127 <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
128 <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
129};
130</programlisting>
131
132 This assumes you have set up an MX record for the address you've
133 set in <link linkend="opt-services.discourse.hostname">hostname</link> and
134 requires proper SPF, DKIM and DMARC configuration to be done for
135 the domain you're sending from, in order for email to be reliably delivered.
136 </para>
137
138 <para>
139 If you want to use a different domain for your outgoing email
140 (for example <literal>example.com</literal> instead of
141 <literal>discourse.example.com</literal>) you should set
142 <xref linkend="opt-services.discourse.mail.notificationEmailAddress" /> and
143 <xref linkend="opt-services.discourse.mail.contactEmailAddress" /> manually.
144 </para>
145
146 <note>
147 <para>
148 Setup of TLS for incoming email is currently only configured
149 automatically when a regular TLS certificate is used, i.e. when
150 <xref linkend="opt-services.discourse.sslCertificate" /> and
151 <xref linkend="opt-services.discourse.sslCertificateKey" /> are
152 set.
153 </para>
154 </note>
155
156 </section>
157
158 <section xml:id="module-services-discourse-settings">
159 <title>Additional settings</title>
160 <para>
161 Additional site settings and backend settings, for which no
162 explicit <productname>NixOS</productname> options are provided,
163 can be set in <xref linkend="opt-services.discourse.siteSettings" /> and
164 <xref linkend="opt-services.discourse.backendSettings" /> respectively.
165 </para>
166
167 <section xml:id="module-services-discourse-site-settings">
168 <title>Site settings</title>
169 <para>
170 <quote>Site settings</quote> are the settings that can be
171 changed through the <productname>Discourse</productname>
172 UI. Their <emphasis>default</emphasis> values can be set using
173 <xref linkend="opt-services.discourse.siteSettings" />.
174 </para>
175
176 <para>
177 Settings are expressed as a Nix attribute set which matches the
178 structure of the configuration in
179 <link xlink:href="https://github.com/discourse/discourse/blob/master/config/site_settings.yml">config/site_settings.yml</link>.
180 To find a setting's path, you only need to care about the first
181 two levels; i.e. its category (e.g. <literal>login</literal>)
182 and name (e.g. <literal>invite_only</literal>).
183 </para>
184
185 <para>
186 Settings containing secret data should be set to an attribute
187 set containing the attribute <literal>_secret</literal> - a
188 string pointing to a file containing the value the option
189 should be set to. See the example.
190 </para>
191 </section>
192
193 <section xml:id="module-services-discourse-backend-settings">
194 <title>Backend settings</title>
195 <para>
196 Settings are expressed as a Nix attribute set which matches the
197 structure of the configuration in
198 <link xlink:href="https://github.com/discourse/discourse/blob/stable/config/discourse_defaults.conf">config/discourse.conf</link>.
199 Empty parameters can be defined by setting them to
200 <literal>null</literal>.
201 </para>
202 </section>
203
204 <section xml:id="module-services-discourse-settings-example">
205 <title>Example</title>
206 <para>
207 The following example sets the title and description of the
208 <productname>Discourse</productname> instance and enables
209 <productname>GitHub</productname> login in the site settings,
210 and changes a few request limits in the backend settings:
211<programlisting>
212services.discourse = {
213 <link linkend="opt-services.discourse.enable">enable</link> = true;
214 <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
215 <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
216 <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
217 admin = {
218 <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
219 <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
220 <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
221 <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
222 };
223 mail.outgoing = {
224 <link linkend="opt-services.discourse.mail.outgoing.serverAddress">serverAddress</link> = "smtp.emailprovider.com";
225 <link linkend="opt-services.discourse.mail.outgoing.port">port</link> = 587;
226 <link linkend="opt-services.discourse.mail.outgoing.username">username</link> = "user@emailprovider.com";
227 <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
228 };
229 <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
230 <link linkend="opt-services.discourse.siteSettings">siteSettings</link> = {
231 required = {
232 title = "My Cats";
233 site_description = "Discuss My Cats (and be nice plz)";
234 };
235 login = {
236 enable_github_logins = true;
237 github_client_id = "a2f6dfe838cb3206ce20";
238 github_client_secret._secret = /run/keys/discourse_github_client_secret;
239 };
240 };
241 <link linkend="opt-services.discourse.backendSettings">backendSettings</link> = {
242 max_reqs_per_ip_per_minute = 300;
243 max_reqs_per_ip_per_10_seconds = 60;
244 max_asset_reqs_per_ip_per_10_seconds = 250;
245 max_reqs_per_ip_mode = "warn+block";
246 };
247 <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
248};
249</programlisting>
250 </para>
251 <para>
252 In the resulting site settings file, the
253 <literal>login.github_client_secret</literal> key will be set
254 to the contents of the
255 <filename>/run/keys/discourse_github_client_secret</filename>
256 file.
257 </para>
258 </section>
259 </section>
260 <section xml:id="module-services-discourse-plugins">
261 <title>Plugins</title>
262 <para>
263 You can install <productname>Discourse</productname> plugins
264 using the <xref linkend="opt-services.discourse.plugins" />
265 option. As long as a plugin supports the standard install
266 method, packaging it should only require fetching its source
267 with an appropriate fetcher.
268 </para>
269
270 <para>
271 Some plugins provide <link
272 linkend="module-services-discourse-site-settings">site
273 settings</link>. Their defaults can be configured using <xref
274 linkend="opt-services.discourse.siteSettings" />, just like
275 regular site settings. To find the names of these settings, look
276 in the <literal>config/settings.yml</literal> file of the plugin
277 repo.
278 </para>
279
280 <para>
281 For example, to add the <link
282 xlink:href="https://github.com/discourse/discourse-spoiler-alert">discourse-spoiler-alert</link>
283 plugin and disable it by default:
284
285<programlisting>
286services.discourse = {
287 <link linkend="opt-services.discourse.enable">enable</link> = true;
288 <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
289 <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
290 <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
291 admin = {
292 <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
293 <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
294 <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
295 <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
296 };
297 mail.outgoing = {
298 <link linkend="opt-services.discourse.mail.outgoing.serverAddress">serverAddress</link> = "smtp.emailprovider.com";
299 <link linkend="opt-services.discourse.mail.outgoing.port">port</link> = 587;
300 <link linkend="opt-services.discourse.mail.outgoing.username">username</link> = "user@emailprovider.com";
301 <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
302 };
303 <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
304 <link linkend="opt-services.discourse.mail.incoming.enable">plugins</link> = [
305 (pkgs.fetchFromGitHub {
306 owner = "discourse";
307 repo = "discourse-spoiler-alert";
308 rev = "e200cfa571d252cab63f3d30d619b370986e4cee";
309 sha256 = "0ya69ix5g77wz4c9x9gmng6l25ghb5xxlx3icr6jam16q14dzc33";
310 })
311 ];
312 <link linkend="opt-services.discourse.siteSettings">siteSettings</link> = {
313 plugins = {
314 spoiler_enabled = false;
315 };
316 };
317 <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
318};
319</programlisting>
320
321 </para>
322 </section>
323</chapter>