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