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