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```