How to Install Discourse Forum with Nginx on Rocky Linux 9

Discourse is an open-source community discussion platform built using the Ruby language. It is designed to work as a forum, chat software, or mailing list. It integrates easily with other platforms and can be extended via plugins.

In this tutorial, you will learn how to install Discourse Forum with the Nginx server on a server running Rocky Linux 9.


  • A server running Rocky Linux 9 with a minimum of 1GB RAM and 1 Core CPU. Discourse setup will automatically create a swap partition on systems with 1GB or less RAM. Therefore, it is recommended to install it on a system with at least 2GB RAM.

  • A non-root user with sudo privileges.

  • A Domain name ( pointing to the server.

  • Everything is updated.

    $ sudo dnf update
  • Few packages that your system needs.

    $ sudo dnf install wget curl nano unzip yum-utils -y

    Some of these packages may already be installed on your system.

Step 1 - Configure Firewall

The first step is to configure the firewall. Rocky Linux uses Firewalld Firewall. Check the firewall's status.

$ sudo firewall-cmd --state

The firewall works with different zones, and the public zone is the default one that we will use. List all the services and ports active on the firewall.

$ sudo firewall-cmd --permanent --list-services

It should show the following output.

cockpit dhcpv6-client ssh

Discourse needs HTTP and HTTPS ports to function. Open them.

$ sudo firewall-cmd --permanent --add-service=http
$ sudo firewall-cmd --permanent --add-service=https

Add masquerade, as the application will contact other instances.

$ sudo firewall-cmd --permanent --add-masquerade

Reload the firewall to apply the changes.

$ sudo firewall-cmd --reload

List all the services again.

$ sudo firewall-cmd --permanent --list-services

You should get the following output.

cockpit dhcpv6-client http https ssh

Configure SELinux

Discourse won't be accessible even if you open the ports due to SELinux's access policy. Configure SELinux to allow network connections.

$ sudo setsebool -P httpd_can_network_connect 1

Step 2 - Install Git

Install Git.

$ sudo dnf install git

Confirm the installation.

$ git --version
git version 2.31.1

Run the following commands to configure the Git installation.

$ git config --global "Your Name"
$ git config --global "[email protected]"

Step 3 - Install Docker

Rocky Linux ships with an older version of Docker. To install the latest version, first, install the official Docker repository.

$ sudo yum-config-manager \
    --add-repo \

Install the latest version of Docker.

$ sudo dnf install docker-ce docker-ce-cli

You may get the following error while trying to install Docker.

 Problem: problem with installed package buildah-1:1.26.2-1.el9_0.x86_64
  - package buildah-1:1.26.2-1.el9_0.x86_64 requires runc >= 1.0.0-26, but none of the providers can be installed
  - package conflicts with runc provided by runc-4:1.1.3-2.el9_0.x86_64
  - package obsoletes runc provided by runc-4:1.1.3-2.el9_0.x86_64
  - cannot install the best candidate for the job

Use the following command if you get the error above.

$ sudo dnf install docker-ce docker-ce-cli --allowerasing

Enable and run the Docker daemon.

$ sudo systemctl enable docker --now

Verify that it is running.

? docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
     Active: active (running) since Sat 2023-01-20 06:49:44 UTC; 6s ago
TriggeredBy: ? docker.socket
   Main PID: 99263 (dockerd)
      Tasks: 8
     Memory: 28.1M
        CPU: 210ms
     CGroup: /system.slice/docker.service
             ??99263 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

By default, Docker requires root privileges. If you want to avoid using sudo every time you run the docker command, add your username to the docker group.

$ sudo usermod -aG docker $(whoami)

You will need to log out of the server and back in as the same user to enable this change or use the following command.

$ su - ${USER}

Confirm that your user is added to the Docker group.

$ groups
navjot wheel docker

Step 4 - Download Discourse

Clone the official Discourse Docker GitHub repository to the /var/discourse directory.

$ sudo git clone /var/discourse

Switch to the Discourse directory.

$ cd /var/discourse

Remove writing and executable permissions from the containers directory.

$ sudo chmod 700 containers

Step 5 - Configure Discourse

Create the configuration file app.yml by copying the sample standalone.yml file.

$ sudo cp samples/standalone.yml containers/app.yml

Open the app.yml for editing.

$ sudo nano containers/app.yml

Set Domain

Set the variable DISCOURSE_HOSTNAME to the domain name, you chose for your forum. If you don't have a domain name, you can use an IP address here.


Configure Exposed ports

Change the line "80:80 to "8080:80". This will change the external HTTP port for Discourse to 8080 since we will use Nginx at port 80. Comment out the "443:443" line since we will install SSL externally.

  - "8080:80"   # http
  #- "443:443" # https

Configure Email IDs for the Administrator

Set the email for your administrator account and the developer using the variable DISCOURSE_DEVELOPER_EMAILS. This step is compulsory otherwise, your forum won't be bootstrapped.

DISCOURSE_DEVELOPER_EMAILS: '[email protected],[email protected]'

Configure SMTP Settings

Fill out the following variables depending on the transactional email service you are using.

DISCOURSE_SMTP_USER_NAME: [email protected]
DISCOURSE_SMTP_PASSWORD: your_smtp_password
#DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)
DISCOURSE_SMTP_DOMAIN:    # (required by some providers)
DISCOURSE_NOTIFICATION_EMAIL: [email protected]    # (address to send notifications from)

Memory Settings (Optional)

If your server has low RAM, you can configure the following variables accordingly to reduce Discourse's memory footprint.

db_shared_buffers: '128MB'

The variable db_shared_buffers is usually set to 25% of the available memory.

GeoLite2 Settings (Optional)

If you want the IP lookup feature on Discourse, signup for the free Maxmind Geolite2 account and get a license key. Paste that license key as the value for the following variable.

DISCOURSE_MAXMIND_LICENSE_KEY: your_maxmind_license_key

Save the file by pressing Ctrl + X and entering Y when prompted.

Step 6 - Install Discourse

Run the following command to bootstrap your Discourse container.

$ sudo ./launcher bootstrap app

Start Discourse application.

$ sudo ./launcher start app

Open port to test Discourse application.

$ sudo firewall-cmd --permanent --add-port=8080/tcp

You can now access the forum by visiting the URLs http://yourserver_IP:8080 or in your browser. You will get the following screen.

Discourse Setup Home

Step 7 - Install Nginx

Rocky Linux 9 ships with an older version of Nginx. You need to download the official Nginx repository to install the latest version.

Create and open the /etc/yum.repos.d/nginx.repo file for creating the official Nginx repository.

$ sudo nano /etc/yum.repos.d/nginx.repo

Paste the following code in it.

name=nginx stable repo

name=nginx mainline repo

Save the file by pressing Ctrl + X and entering Y when prompted.

Install the Nginx server.

$ sudo dnf install nginx

Verify the installation.

$ nginx -v
nginx version: nginx/1.22.1

Enable and start the Nginx server.

$ sudo systemctl enable nginx --now

Check the status of the server.

$ sudo systemctl status nginx
? nginx.service - nginx - high performance web server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
     Active: active (running) since Sun 2023-01-20 07:49:55 UTC; 1s ago
    Process: 230797 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
   Main PID: 230798 (nginx)
      Tasks: 3 (limit: 12355)
     Memory: 2.8M
        CPU: 13ms
     CGroup: /system.slice/nginx.service
             ??230798 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
             ??230799 "nginx: worker process"
             ??230800 "nginx: worker process"

Step 8 - Install SSL

To install an SSL certificate using Let's Encrypt, we need to install the Certbot tool.

We will use the Snapd package installer for that. Since Rocky Linux doesn't ship with it, install the Snapd installer. It requires the EPEL repository to work.

$ sudo dnf install epel-release

Install Snapd.

$ sudo dnf install snapd

Enable and Start the Snap service.

$ sudo systemctl enable snapd --now

Install the Snap core package, and ensure that your version of Snapd is up to date.

$ sudo snap install core && sudo snap refresh core

Create necessary links for Snapd to work.

$ sudo ln -s /var/lib/snapd/snap /snap
$ echo 'export PATH=$PATH:/var/lib/snapd/snap/bin' | sudo tee -a /etc/profile.d/

Issue the following command to install Certbot.

$ sudo snap install --classic certbot

Use the following command to ensure that the Certbot command can be run by creating a symbolic link to the /usr/bin directory.

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Verify the installation.

$ certbot --version
certbot 1.32.2

Generate the SSL certificate.

$ sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m [email protected] -d

The above command will download a certificate to the /etc/letsencrypt/live/ directory on your server.

Generate a Diffie-Hellman group certificate.

$ sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096

To check whether the SSL renewal is working fine, do a dry run of the process.

$ sudo certbot renew --dry-run

If you see no errors, you are all set. Your certificate will renew automatically.

Step 9 - Configure Nginx

Create and open the file /etc/nginx/conf.d/discourse.conf for editing.

$ sudo nano /etc/nginx/conf.d/discourse.conf

Paste the following code in it.

# enforce HTTPS
server {
    listen       80; 
    listen 		[::]:80;
    location / { return 301 https://$host$request_uri; }
server {
    listen       443 ssl http2;
    listen 		[::]:443 ssl http2;

    access_log  /var/log/nginx/discourse.access.log;
    error_log   /var/log/nginx/discourse.error.log;
    # SSL
    ssl_certificate         /etc/letsencrypt/live/;
    ssl_certificate_key     /etc/letsencrypt/live/;
    ssl_trusted_certificate /etc/letsencrypt/live/;
    ssl_session_timeout  5m;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    http2_push_preload on; # Enable HTTP/2 Server Push
    # Enable TLSv1.3's 0-RTT. Use $ssl_early_data when reverse proxying to
    # prevent replay attacks.
    # @see:
    ssl_early_data on;
    # Security / XSS Mitigation Headers
    # NOTE: X-Frame-Options may cause issues with the webOS app
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Early-Data $tls1_3_early_data;
    client_max_body_size 100m;
    location / {
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;

# This block is useful for debugging TLS v1.3. Please feel free to remove this
# and use the `$ssl_early_data` variable exposed by NGINX directly should you
# wish to do so.
map $ssl_early_data $tls1_3_early_data {
    "~." $ssl_early_data;
    default "";

Save the file by pressing Ctrl + X and entering Y when prompted once finished.

Open the file /etc/nginx/nginx.conf for editing.

$ sudo nano /etc/nginx/nginx.conf

Add the following line before the line include /etc/nginx/conf.d/*.conf;.

server_names_hash_bucket_size  64;

Save the file by pressing Ctrl + X and entering Y when prompted.

Verify the Nginx configuration file syntax.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart the Nginx service to enable the new configuration.

$ sudo systemctl restart nginx

Step 10 - Access and Finish Discourse Install

You can access the forum by visiting the URL in your browser. You will get the following screen.

Discourse Setup Home

Click the Register button to proceed. The email id set in the app.yml file will be pre-filled for you.

Discourse Create Admin Account

Click the Register button to register the administrator account. You will proceed to the email confirmation screen.

Discourse Email Confirm

If your SMTP settings are correct, you will receive a mail to activate the account. Click the link from your email to finish setting up the account.

Discourse Confirmation Email

Click the Activate button to finish the installation.

Discourse Account Activate

You will get to the Discourse's Setup Wizard screen. Fill in your forum name, and description, and choose the language. Click the Next button to proceed.

Discourse Setup Wizard

Next, you will be taken to the Member Experience page. Choose the options according to your requirement and click the Next button to proceed.

Discourse Member Experience Setup

Next, you will be taken to the setup completion page. You can either choose to configure more or just Jump in and start using the forum. These settings can still be configured using the administrator settings.

Discourse Site Ready

We will choose Configure more for our tutorial. You will be taken to the page and asked to set logos and banner for your forum. Click the Next button to proceed.

Next, you will be asked to set basic styling options for the forum's front end. Click the Next button to proceed.

Discourse Configure Look and Feel

Next, you will be taken to the Organization configuration page. Fill in the appropriate information.

Discourse Organization Setup

Click the Jump in! button to proceed. You will be taken to the forum home. Your Discourse forum is ready for use.

Discourse Homepage

Step 11 - Discourse Commands

Activate Discourse Administrator from the command-line

If you don't receive the activation e-mail, you can activate the administrator account from the command line.

Switch to the Discourse directory.

$ cd /var/discourse

Enter the Discourse container shell.

$ sudo ./launcher enter app

Enter the command rails c to access the Rails command prompt.

root@discourse-app:/var/www/discourse# rails c

You will see the following prompt.

[1] pry(main)> 

Enter the command to locate the administrator account.

[1] pry(main)>  User.find_by_email("[email protected]")
=> #<User:0x00007fdf020229f0
 id: 1,
 username: "username",
 created_at: Fri, 20 Jan 2023 08:14:36.735552000 UTC +00:00,
 updated_at: Fri, 20 Jan 2023 14:14:12.094234000 UTC +00:00,
 name: nil,
 seen_notification_id: 4,
 last_posted_at: nil,
 password_hash: "98c774785bda45b4edbaea90eeb3bd5da00f65487ba6d0b3930082ce098bf174",
 salt: "ca175ea7f5653ce8122b76b68b549936",
 active: true,
 username_lower: "username",
 last_seen_at: Fri, 20 Jan 2023 23:01:21.001705000 UTC +00:00,
 admin: true,
 last_emailed_at: Fri, 20 Jan 2023 09:48:47.896200000 UTC +00:00,
 trust_level: 1,
 approved: false,
 approved_by_id: nil,
 approved_at: nil,
 previous_visit_at: Fri, 20 Jan 2023 21:28:23.665502000 UTC +00:00,
 suspended_at: nil,
 suspended_till: nil,
 date_of_birth: nil,
 views: 0,
 flag_level: 0,
 ip_address: #<IPAddr: IPv4:>,
 moderator: false,
 title: nil,
 uploaded_avatar_id: nil,

Enter q to return to the prompt and enter the following commands in sequence.

[2] pry(main)> user.approved = true
[3] pry(main)>
[4] pry(main)> EmailToken.confirm(user.email_tokens.first.token)

Type exit twice to return to the shell. Your administrator account is activated and ready for use.

Backup Discourse

Discourse backups the database every 7 days by default. You can find the backup files at the `/var/discourse/shared/standalone/backups/default directory.

$ ls /var/discourse/shared/standalone/backups/default -al
total 1480
drwxr-xr-x. 2 navjot tape    4096 Jan 21 03:34 .
drwxr-xr-x. 3 navjot tape    4096 Jan 20 12:01 ..
-rw-r--r--. 1 navjot tape 1503748 Jan 21 03:34 howtoforge-forums-2023-01-21-033439-v20230119094939.tar.gz

You can configure backup settings by visiting the Admin >> Settings >> Backups section. Discourse gives you the option to store the backups locally or to an Amazon S3-style cloud storage.

Discourse Backups Settings

You can download the backups from the Admin >> Backups page. Restore is disabled by default. If you want to restore the database, you need to enable the allow restore option from Admin >> Settings >> Backups section.

Discourse Backups Page

Upgrade Discourse

To upgrade the forum, you can use one of two ways. The first way is to upgrade it via the administrator dashboard. The second method is to use a command line.

Switch to the Discourse directory.

$ cd /var/discourse

Update the Discourse installation by grabbing the latest files from GitHub.

$ git pull

Rebuild Discourse.

$ sudo ./launcher rebuild app

You need to rebuild Discourse every time you make any changes in the app.yml file. After making the changes, run the command above. It destroys the old container, bootstraps a new one, and starts it.

Stop Discourse

$ sudo ./launcher stop app

View Discourse Logs

$ sudo ./launcher logs app

You can also view the detailed labs by visiting the URL in your browser. You will get a similar screen.

Discourse Logs Page

Rails / Unicorn Logs

Ruby on Rails contains a very verbose log that is written to disk. Additionally, the web server unicorn also logs to disk. You can view them at the var/discourse/shared/standalone/log/rails directory.

$ ls /var/discourse/shared/standalone/log/rails -al
total 552
drwxr-xr-x. 2 navjot tape   4096 Jan 20 07:09 .
drwxr-xr-x. 4 root   root   4096 Jan 20 07:09 ..
-rw-r--r--. 1 navjot tape      0 Jan 20 07:09 production_errors.log
-rw-r--r--. 1 navjot tape 361031 Jan 21 11:46 production.log
-rw-r--r--. 1 navjot tape      0 Jan 20 07:09 sidekiq.log
-rw-r--r--. 1 navjot tape  60919 Jan 20 08:42 unicorn.stderr.log
-rw-r--r--. 1 navjot tape 119403 Jan 21 03:34 unicorn.stdout.log

Nginx Logs

Discourse runs an Nginx server inside a docker container. You can view the logs at the following location.

$ ls /var/discourse/shared/standalone/log/var-log/nginx -al
total 1852
drwxr-xr-x. 2   33 tape    4096 Jan 20 07:39 .
drwxrwxr-x. 5 root adm     4096 Jan 21 08:04 ..
-rw-r--r--. 1   33 tape 1878744 Jan 21 12:18 access.log
-rw-r--r--. 1   33 tape    1017 Jan 20 07:45 error.log

Redis Logs

You can get logs for the Redis database server at the following location. The most recent log entries will be in the current file.

$ ls /var/discourse/shared/standalone/log/var-log/redis -al
total 140
drwxr-xr-x. 2 root root   4096 Jan 20 07:39 .
drwxrwxr-x. 5 root adm    4096 Jan 21 08:04 ..
-rw-r--r--. 1 root root 127002 Jan 21 12:17 current
-rw-------. 1 root root      0 Jan 20 07:39 lock

PostgreSQL Logs

You can view the PostgreSQL database logs at the following location.

ls /var/discourse/shared/standalone/log/var-log/postgres -al
total 20
drwxr-xr-x. 2 root root  4096 Jan 20 07:39 .
drwxrwxr-x. 5 root adm   4096 Jan 21 08:04 ..
-rw-r--r--. 1 root root 11400 Jan 21 03:34 current
-rw-------. 1 root root     0 Jan 20 07:39 lock

The most recent entries are in the current file in that directory.

The remaining can be found in the /var/discourse/shared/standalone/log/var-log itself.

ls /var/discourse/shared/standalone/log/var-log -al
total 100
drwxrwxr-x. 5 root adm   4096 Jan 21 08:04 .
drwxr-xr-x. 4 root root  4096 Jan 20 07:09 ..
-rw-r--r--. 1 root adm  40943 Jan 21 12:17 auth.log
-rw-r--r--. 1 root adm      0 Jan 20 08:41 kern.log
-rw-r-----. 1 root adm    750 Jan 21 08:04 messages
drwxr-xr-x. 2   33 tape  4096 Jan 20 07:39 nginx
drwxr-xr-x. 2 root root  4096 Jan 20 07:39 postgres
drwxr-xr-x. 2 root root  4096 Jan 20 07:39 redis
-rw-r--r--. 1 root adm  23041 Jan 21 12:17 syslog
-rw-r-----. 1 root adm     70 Jan 21 08:04 user.log


This concludes the tutorial. You have installed the Discourse forum using Docker behind the Nginx proxy web server on a Rocky Linux 9 server. If you have any questions, post them in the comments below.

Share this page:

0 Comment(s)