summary refs log tree commit diff
path: root/nixos/modules/services/web-apps/discourse.xml
blob: ad9b65abf51e0406635da6e7ab1443d5505cc1a7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
<chapter xmlns="http://docbook.org/ns/docbook"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:xi="http://www.w3.org/2001/XInclude"
         version="5.0"
         xml:id="module-services-discourse">
 <title>Discourse</title>
 <para>
   <link xlink:href="https://www.discourse.org/">Discourse</link> is a
   modern and open source discussion platform.
 </para>

 <section xml:id="module-services-discourse-basic-usage">
   <title>Basic usage</title>
   <para>
     A minimal configuration using Let's Encrypt for TLS certificates looks like this:
<programlisting>
services.discourse = {
  <link linkend="opt-services.discourse.enable">enable</link> = true;
  <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
  admin = {
    <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
    <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
    <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
    <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
  };
  <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
};
<link linkend="opt-security.acme.defaults.email">security.acme.email</link> = "me@example.com";
<link linkend="opt-security.acme.acceptTerms">security.acme.acceptTerms</link> = true;
</programlisting>
   </para>

   <para>
     Provided a proper DNS setup, you'll be able to connect to the
     instance at <literal>discourse.example.com</literal> and log in
     using the credentials provided in
     <literal>services.discourse.admin</literal>.
   </para>
 </section>

 <section xml:id="module-services-discourse-tls">
   <title>Using a regular TLS certificate</title>
   <para>
     To set up TLS using a regular certificate and key on file, use
     the <xref linkend="opt-services.discourse.sslCertificate" />
     and <xref linkend="opt-services.discourse.sslCertificateKey" />
     options:

<programlisting>
services.discourse = {
  <link linkend="opt-services.discourse.enable">enable</link> = true;
  <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
  <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
  <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
  admin = {
    <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
    <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
    <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
    <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
  };
  <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
};
</programlisting>

   </para>
 </section>

 <section xml:id="module-services-discourse-database">
   <title>Database access</title>
   <para>
     <productname>Discourse</productname> uses
     <productname>PostgreSQL</productname> to store most of its
     data. A database will automatically be enabled and a database
     and role created unless <xref
     linkend="opt-services.discourse.database.host" /> is changed from
     its default of <literal>null</literal> or <xref
     linkend="opt-services.discourse.database.createLocally" /> is set
     to <literal>false</literal>.
   </para>

   <para>
     External database access can also be configured by setting
     <xref linkend="opt-services.discourse.database.host" />, <xref
     linkend="opt-services.discourse.database.username" /> and <xref
     linkend="opt-services.discourse.database.passwordFile" /> as
     appropriate. Note that you need to manually create a database
     called <literal>discourse</literal> (or the name you chose in
     <xref linkend="opt-services.discourse.database.name" />) and
     allow the configured database user full access to it.
   </para>
 </section>

 <section xml:id="module-services-discourse-mail">
   <title>Email</title>
   <para>
     In addition to the basic setup, you'll want to configure an SMTP
     server <productname>Discourse</productname> can use to send user
     registration and password reset emails, among others. You can
     also optionally let <productname>Discourse</productname> receive
     email, which enables people to reply to threads and conversations
     via email.
   </para>

   <para>
     A basic setup which assumes you want to use your configured <link
     linkend="opt-services.discourse.hostname">hostname</link> as
     email domain can be done like this:

<programlisting>
services.discourse = {
  <link linkend="opt-services.discourse.enable">enable</link> = true;
  <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
  <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
  <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
  admin = {
    <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
    <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
    <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
    <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
  };
  mail.outgoing = {
    <link linkend="opt-services.discourse.mail.outgoing.serverAddress">serverAddress</link> = "smtp.emailprovider.com";
    <link linkend="opt-services.discourse.mail.outgoing.port">port</link> = 587;
    <link linkend="opt-services.discourse.mail.outgoing.username">username</link> = "user@emailprovider.com";
    <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
  };
  <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
  <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
};
</programlisting>

     This assumes you have set up an MX record for the address you've
     set in <link linkend="opt-services.discourse.hostname">hostname</link> and
     requires proper SPF, DKIM and DMARC configuration to be done for
     the domain you're sending from, in order for email to be reliably delivered.
   </para>

   <para>
     If you want to use a different domain for your outgoing email
     (for example <literal>example.com</literal> instead of
     <literal>discourse.example.com</literal>) you should set
     <xref linkend="opt-services.discourse.mail.notificationEmailAddress" /> and
     <xref linkend="opt-services.discourse.mail.contactEmailAddress" /> manually.
   </para>

   <note>
     <para>
       Setup of TLS for incoming email is currently only configured
       automatically when a regular TLS certificate is used, i.e. when
       <xref linkend="opt-services.discourse.sslCertificate" /> and
       <xref linkend="opt-services.discourse.sslCertificateKey" /> are
       set.
     </para>
   </note>

 </section>

 <section xml:id="module-services-discourse-settings">
   <title>Additional settings</title>
   <para>
     Additional site settings and backend settings, for which no
     explicit <productname>NixOS</productname> options are provided,
     can be set in <xref linkend="opt-services.discourse.siteSettings" /> and
     <xref linkend="opt-services.discourse.backendSettings" /> respectively.
   </para>

   <section xml:id="module-services-discourse-site-settings">
     <title>Site settings</title>
     <para>
       <quote>Site settings</quote> are the settings that can be
       changed through the <productname>Discourse</productname>
       UI. Their <emphasis>default</emphasis> values can be set using
       <xref linkend="opt-services.discourse.siteSettings" />.
     </para>

     <para>
       Settings are expressed as a Nix attribute set which matches the
       structure of the configuration in
       <link xlink:href="https://github.com/discourse/discourse/blob/master/config/site_settings.yml">config/site_settings.yml</link>.
       To find a setting's path, you only need to care about the first
       two levels; i.e. its category (e.g. <literal>login</literal>)
       and name (e.g. <literal>invite_only</literal>).
     </para>

     <para>
       Settings containing secret data should be set to an attribute
       set containing the attribute <literal>_secret</literal> - a
       string pointing to a file containing the value the option
       should be set to. See the example.
     </para>
   </section>

   <section xml:id="module-services-discourse-backend-settings">
     <title>Backend settings</title>
     <para>
       Settings are expressed as a Nix attribute set which matches the
       structure of the configuration in
       <link xlink:href="https://github.com/discourse/discourse/blob/stable/config/discourse_defaults.conf">config/discourse.conf</link>.
       Empty parameters can be defined by setting them to
       <literal>null</literal>.
     </para>
   </section>

   <section xml:id="module-services-discourse-settings-example">
     <title>Example</title>
     <para>
       The following example sets the title and description of the
       <productname>Discourse</productname> instance and enables
       <productname>GitHub</productname> login in the site settings,
       and changes a few request limits in the backend settings:
<programlisting>
services.discourse = {
  <link linkend="opt-services.discourse.enable">enable</link> = true;
  <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
  <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
  <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
  admin = {
    <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
    <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
    <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
    <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
  };
  mail.outgoing = {
    <link linkend="opt-services.discourse.mail.outgoing.serverAddress">serverAddress</link> = "smtp.emailprovider.com";
    <link linkend="opt-services.discourse.mail.outgoing.port">port</link> = 587;
    <link linkend="opt-services.discourse.mail.outgoing.username">username</link> = "user@emailprovider.com";
    <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
  };
  <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
  <link linkend="opt-services.discourse.siteSettings">siteSettings</link> = {
    required = {
      title = "My Cats";
      site_description = "Discuss My Cats (and be nice plz)";
    };
    login = {
      enable_github_logins = true;
      github_client_id = "a2f6dfe838cb3206ce20";
      github_client_secret._secret = /run/keys/discourse_github_client_secret;
    };
  };
  <link linkend="opt-services.discourse.backendSettings">backendSettings</link> = {
    max_reqs_per_ip_per_minute = 300;
    max_reqs_per_ip_per_10_seconds = 60;
    max_asset_reqs_per_ip_per_10_seconds = 250;
    max_reqs_per_ip_mode = "warn+block";
  };
  <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
};
</programlisting>
     </para>
     <para>
       In the resulting site settings file, the
       <literal>login.github_client_secret</literal> key will be set
       to the contents of the
       <filename>/run/keys/discourse_github_client_secret</filename>
       file.
     </para>
   </section>
 </section>
  <section xml:id="module-services-discourse-plugins">
    <title>Plugins</title>
    <para>
      You can install <productname>Discourse</productname> plugins
      using the <xref linkend="opt-services.discourse.plugins" />
      option. Pre-packaged plugins are provided in
      <literal>&lt;your_discourse_package_here&gt;.plugins</literal>. If
      you want the full suite of plugins provided through
      <literal>nixpkgs</literal>, you can also set the <xref
      linkend="opt-services.discourse.package" /> option to
      <literal>pkgs.discourseAllPlugins</literal>.
    </para>

    <para>
      Plugins can be built with the
      <literal>&lt;your_discourse_package_here&gt;.mkDiscoursePlugin</literal>
      function. Normally, it should suffice to provide a
      <literal>name</literal> and <literal>src</literal> attribute. If
      the plugin has Ruby dependencies, however, they need to be
      packaged in accordance with the <link
      xlink:href="https://nixos.org/manual/nixpkgs/stable/#developing-with-ruby">Developing
      with Ruby</link> section of the Nixpkgs manual and the
      appropriate gem options set in <literal>bundlerEnvArgs</literal>
      (normally <literal>gemdir</literal> is sufficient). A plugin's
      Ruby dependencies are listed in its
      <filename>plugin.rb</filename> file as function calls to
      <literal>gem</literal>. To construct the corresponding
      <filename>Gemfile</filename> manually, run <command>bundle
      init</command>, then add the <literal>gem</literal> lines to it
      verbatim.
    </para>

    <para>
      Much of the packaging can be done automatically by the
      <filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename>
      script - just add the plugin to the <literal>plugins</literal>
      list in the <function>update_plugins</function> function and run
      the script:
      <programlisting language="bash">
./update.py update-plugins
</programlisting>
    </para>

    <para>
      Some plugins provide <link
      linkend="module-services-discourse-site-settings">site
      settings</link>. Their defaults can be configured using <xref
      linkend="opt-services.discourse.siteSettings" />, just like
      regular site settings. To find the names of these settings, look
      in the <literal>config/settings.yml</literal> file of the plugin
      repo.
    </para>

    <para>
      For example, to add the <link
      xlink:href="https://github.com/discourse/discourse-spoiler-alert">discourse-spoiler-alert</link>
      and <link
      xlink:href="https://github.com/discourse/discourse-solved">discourse-solved</link>
      plugins, and disable <literal>discourse-spoiler-alert</literal>
      by default:

<programlisting>
services.discourse = {
  <link linkend="opt-services.discourse.enable">enable</link> = true;
  <link linkend="opt-services.discourse.hostname">hostname</link> = "discourse.example.com";
  <link linkend="opt-services.discourse.sslCertificate">sslCertificate</link> = "/path/to/ssl_certificate";
  <link linkend="opt-services.discourse.sslCertificateKey">sslCertificateKey</link> = "/path/to/ssl_certificate_key";
  admin = {
    <link linkend="opt-services.discourse.admin.email">email</link> = "admin@example.com";
    <link linkend="opt-services.discourse.admin.username">username</link> = "admin";
    <link linkend="opt-services.discourse.admin.fullName">fullName</link> = "Administrator";
    <link linkend="opt-services.discourse.admin.passwordFile">passwordFile</link> = "/path/to/password_file";
  };
  mail.outgoing = {
    <link linkend="opt-services.discourse.mail.outgoing.serverAddress">serverAddress</link> = "smtp.emailprovider.com";
    <link linkend="opt-services.discourse.mail.outgoing.port">port</link> = 587;
    <link linkend="opt-services.discourse.mail.outgoing.username">username</link> = "user@emailprovider.com";
    <link linkend="opt-services.discourse.mail.outgoing.passwordFile">passwordFile</link> = "/path/to/smtp_password_file";
  };
  <link linkend="opt-services.discourse.mail.incoming.enable">mail.incoming.enable</link> = true;
  <link linkend="opt-services.discourse.mail.incoming.enable">plugins</link> = with config.services.discourse.package.plugins; [
    discourse-spoiler-alert
    discourse-solved
  ];
  <link linkend="opt-services.discourse.siteSettings">siteSettings</link> = {
    plugins = {
      spoiler_enabled = false;
    };
  };
  <link linkend="opt-services.discourse.secretKeyBaseFile">secretKeyBaseFile</link> = "/path/to/secret_key_base_file";
};
</programlisting>

    </para>
  </section>
</chapter>