How to Install Discourse Forum with Nginx and Free Let's Encrypt SSL on Debian 11

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 its functionality can be expanded with plugins.

In this tutorial, you will learn how to install Discourse Forum with the Nginx server on a Debian 11 based server.


  • A server running Debian 11 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 apt update && sudo apt upgrade
  • Few packages that your system needs.

    $ sudo apt install nano ufw software-properties-common dirmngr apt-transport-https gnupg2 ca-certificates lsb-release debian-archive-keyring -y

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

Step 1 - Configure Firewall

The first step is to configure the firewall. Debian comes with ufw (Uncomplicated Firewall).

Check if the firewall is running.

$ sudo ufw status

You should get the following output.

Status: inactive

Allow SSH port so that the firewall doesn't break the current connection on enabling it.

$ sudo ufw allow OpenSSH

Allow HTTP and HTTPS ports as well.

$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp

Enable the Firewall

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Check the status of the firewall again.

$ sudo ufw status

You should see a similar output.

Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)
80/tcp (v6)                ALLOW       Anywhere (v6)
443/tcp (v6)               ALLOW       Anywhere (v6)

Step 2 - Install Git

Install Git using the default Appstream.

$ sudo dnf install git

Confirm the installation.

$ git --version
git version 2.30.2

Run the following commands to configure the Git installation.

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

Step 3 - Install Docker

To install the latest version of Docker, add Docker's official GPG key.

$ curl -fsSL | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Install the official Docker repository.

$ echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Update Debian system repositories.

$ sudo apt update

Install the latest version of Docker.

$ sudo apt install docker-ce docker-ce-cli

Verify that Docker is running.

$ sudo systemctl status docker
? docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2022-02-05 13:32:54 UTC; 1h ago
TriggeredBy: ? docker.socket
   Main PID: 5818 (dockerd)
      Tasks: 26
     Memory: 1.4G
        CPU: 5min 34.561s
     CGroup: /system.slice/docker.service
             ?? 5818 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
             ??12162 /usr/bin/docker-proxy -proto tcp -host-ip -host-port 8080 -container-ip -contai>
             ??12169 /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 8080 -container-ip -container-p>

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)

To enable this change, you will need to log out of the server and back in as the same user.

Step 4 - Download Discourse

Create the root directory for Discourse.

$ sudo mkdir /var/discourse

Clone the official Discourse Docker Github repository.

$ sudo git clone /var/discourse

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 SMTP Settings

Fill out the following variables depending upon the transactional email service you are using. Set the email for your administrator account using the variable DISCOURSE_DEVELOPER_EMAILS. This step is compulsory otherwise, your forum won't be bootstrapped.

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

You can access the forum by visiting the URLs http://yourserver_IP:8080 or 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. You can either skip it to proceed directly to the forum or go through the entire wizard.

Discourse Setup Wizard

Your Discourse forum is ready. The next step is installing SSL and putting the forum behind the Nginx server.

Discourse Homepage

Step 7 - 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 most Debian servers don't ship with it, install the Snapd installer.

$ sudo apt install snapd

Ensure that your version of Snapd is up to date.

$ sudo snap install core && sudo snap refresh core

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.22.0

Generate the SSL certificate.

$ sudo certbot certonly --standalone --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 -out /etc/ssl/certs/dhparam.pem 2048

Create a challenge webroot directory for Let's Encrypt auto-renewal.

$ sudo mkdir -p /var/lib/letsencrypt

Create a Cron Job to renew the SSL. It will run every day to check the certificate and renew if needed. For that, first, create the file /etc/cron.daily/certbot-renew and open it for editing.

$ sudo nano /etc/cron.daily/certbot-renew

Paste the following code.

certbot renew --cert-name --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx"

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

Change the permissions on the task file to make it executable.

$ sudo chmod +x /etc/cron.daily/certbot-renew

Step 8 - Install and Configure Nginx

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

Import the official Nginx signing key.

$ curl | gpg --dearmor \
	 | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

Add the repository for Nginx's stable version.

$ echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \ `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

Update the Debian repositories.

$ sudo apt update

Install Nginx.

$ sudo apt install nginx

Verify the installation. Make sure you use sudo every time you run the Nginx command on Debian. Otherwise, it won't work.

$ sudo nginx -v
nginx version: nginx/1.20.2

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;
    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:MozSSL: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;
    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;

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

Start the Nginx service to enable the new configuration.

$ sudo systemctl start nginx

Step 9 - Discourse Commands

Activate Discourse Administrator from the command-line

If you don't receive the activation email, 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.

[email protected]:/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:0x00005564492032a0
 id: 1,
 username: "username",
 created_at: Sun, 06 Feb 2022 14:46:58.451302000 UTC +00:00,
 updated_at: Sun, 06 Feb 2022 14:54:17.079564000 UTC +00:00,
 name: nil,
 seen_notification_id: 4,
 last_posted_at: nil,
 password_hash: "[FILTERED]",
 salt: "20d6012d3c98da70896dcfc27bc9d264",
 active: true,
 username_lower: "username",
 last_seen_at: Mon, 07 Feb 2022 08:34:12.435844000 UTC +00:00,
 admin: true,
 last_emailed_at: Sun, 06 Feb 2022 14:47:00.694121000 UTC +00:00,
 trust_level: 1,
 approved: false,
 approved_by_id: nil,
 approved_at: nil,
 previous_visit_at: Sun, 06 Feb 2022 15:40:35.804941000 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: 3,

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.

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

View Discourse Logs

$ sudo ./launcher logs


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

Share this page:

0 Comment(s)

Add comment

Please register in our forum first to comment.