How to completely install Discourse on Ubutu 16.4 with Nginx

Step 1

Step 1 - Install Docker on Ubuntu 16.04

The Discourse software is written in Ruby and Javascript, using PostgreSQL as the main database, and Redis as a cache and for transient data. We will install Discourse under the Docker container.

The installation process will be carried out on Ubuntu 16.04. So to begin with, install Docker using the command below.

wget -qO- | sh

After the installation is complete, check the docker service and make sure it’s already running on the system.

systemctl status docker

And also make sure that Docker is on the startup boot app.

systemctl is-enabled docker

Step 2 - Install and configure discourse forum

In this step, we will install and configure the Discourse software. We will download the discourse docker-compose script, configure according to our requirement, and then create new Docker container for discourse forum software.

Create new ‘/var/discourse’ directory and clone the installer script.

mkdir -p /var/discourse
git clone /var/discourse

Now go to the ‘discourse’ directory and copy sample of the docker-compose file.

cd /var/discourse
cp samples/standalone.yml containers/app.yml

Edit the ‘app.yml’ file using vim editor.

vim containers/app.yml

Configure Docker port mapping

By default, all HTTP and HTTPS requests will be handled by the Docker proxy. And for this guide, we will use Nginx for this purpose - all HTTP and HTTPS will be handled by the Nginx web server.

So we need to change the docker port mapping configuration. The Discourse container will only have the HTTP connection, and the host will be opening new port ‘2045’ and mapping to the container port 80.

Uncomment the HTTPS line and change the HTTP line with new port ‘2045’.


  • “2045:80” # http

- “443:443” # https

Discourse domain name configuration

Type your own domain name for discourse installed on the ‘DISCOURSE_HOSTNAME’ line as below.


And type your email address in the ‘DISCOURSE_DEVELOPER_EMAIL’ line.


SMTP configuration

This is the most important configuration for Discourse Software. Make sure you have the SMTP account for Discourse installation - you can buy or try free-tier SMTP account from Mailgun, Mailjet or SendGrid.

In this tutorial, we will be using the free-tier SMTP account from Mailgun. Uncomment SMTP configuration and type your account as below.

Save and exit.

Next, build new Discourse Docker image based on app.yml template configuration.

sudo ./launcher bootstrap app

After that, start/initialize new Discourse container using the command below.

sudo ./launcher start app

Start the app|500x106

The Discourse container should be up and running - check it using docker command.

docker ps -a

Check the host open port using netstat, and make sure new docker-proxy port 2045 is on the list.
Source from:

Nginx config file for Discourse:

# Set discourse upstream paths
upstream discourse-http {
    server unix:/var/discourse/shared/web-only/nginx.http.sock;
upstream discourse-https {
    server unix:/var/discourse/shared/web-only/nginx.https.sock;

# Rewrite http requests to https
server {
    listen 80 default_server;

    rewrite ^$request_uri? permanent;

# Our HTTPS Server
# We don't really need much config here since everything gets passed to the docker instance
# We're just here to forward requests

server {

    listen 443 ssl http2;
    # listen on IPv6 as well
    listen [::]:443 ssl http2;

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    # we're using Discourse's default SSL location here but it can be changed to something more standard like /etc/ssl/
    ssl_certificate /var/discourse/shared/web-only/ssl/ssl.crt;
    ssl_certificate_key /var/discourse/shared/web-only/ssl/ssl.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /var/discourse/shared/web-only/ssl/dhparams.pem;

    # intermediate configuration. tweak to your needs.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    # OCSP Stapling ---
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling on;
    ssl_stapling_verify on;

    ## verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;

    resolver <IP DNS resolver>;

    server_name _ ;
    server_tokens off;

    keepalive_timeout 65;

    # maximum file upload size (keep up to date when changing the corresponding Discourse)
    client_max_body_size 10m;

    # Pass everything to Discourse
    location / {
        proxy_set_header    Host                 $http_host;
        proxy_set_header    X-Real-IP            $remote_addr;
        proxy_set_header    X-Forwarded-For      $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto    $scheme;
        # Discourse unix socket
        proxy_pass $scheme://discourse-$scheme;

These updates are **not** picked up inadmin/upgrade` , which is why you’ll occasionally need to do this additional step.

  1. Discourse itself should be updated about twice a month, by clicking the “Update to Latest Version” button in your admin dashboard ( admin/upgrade ). We do beta releases roughly once every week.
  2. Every two months we recommend SSH’ing into your web server (using putty or your favorite SSH client) and doing
cd /var/discourse
git pull
./launcher rebuild app
  1. As for Ubuntu updates, make sure you have automatic security updates enabled for your Ubuntu! The command is:
dpkg-reconfigure -plow unattended-upgrades

However, that just covers critical security updates. Every so often you should get all the OS updates like so:

apt-get update
apt-get dist-upgrade

Due to the way docker packaging has changed you may also have to update your docker from lxc-docker or docker-engine packages. One way to do that is via the Docker script here (it will warn about an existing install but should upgrade ok):

wget -qO- | sh

This will now use the docker-ce main versions.

This is completely safe, we have never seen anything get broken by base Ubuntu updates.

To summarize:

  • update Discourse twice a month via web updater
  • update the container every two months
  • update the OS every six months

You could double these numbers and still be fairly safe , e.g. update Discourse once a month, container every 4 months, OS once every 12 months, and so on.

But you really, really want automatic security updates enabled in Ubuntu, as listed above – all our Digital Ocean “one-time installs” already have automatic security updates set up.


If for some reason that does not work, SSH into your server and issue these commands at a console:

cd /var/discourse
git pull
./launcher rebuild app

If you are somehow on a pre-V1 version of Discourse, you’ll need to upgrade to V1.

## this is the all-in-one, standalone Discourse Docker container template
## After making changes to this file, you MUST rebuild
## /var/discourse/launcher rebuild app
## visit to validate this file as needed

  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

## which TCP/IP ports should this container expose?
## If you want Discourse to share a port with another webserver like Apache or nginx,
## see for details
  - "8080:80"   # http
#  - "443:443" # https

  db_default_text_search_config: "pg_catalog.english"

  ## Set db_shared_buffers to a max of 25% of the total memory.
  ## will be set automatically by bootstrap based on detected RAM, or you can override
  #db_shared_buffers: "256MB"

  ## can improve sorting performance, but adds memory usage per-connection
  #db_work_mem: "40MB"

  ## Which Git revision should this container use? (default: tests-passed)
  #version: tests-passed

  LANG: en_US.UTF-8

  ## How many concurrent web requests are supported? Depends on memory and CPU cores.
  ## will be set automatically by bootstrap based on detected CPUs, or you can override

  ## TODO: The domain name this Discourse instance will respond to
  ## Required. Discourse will not work with a bare IP number.

  ## Uncomment if you want the container to be started with the same
  ## hostname (-h option) as specified above (default "$hostname-$config")

  ## TODO: List of comma delimited emails that will be made admin and developer
  ## on initial signup example ','

  ## TODO: The SMTP mail server used to validate new accounts and send notifications
  # SMTP ADDRESS, username, and password are required
  # WARNING the char '#' in SMTP password can cause problems!
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)

  ## If you added the Lets Encrypt template, uncomment below to get a free SSL certificate

  ## The CDN address for this Discourse instance (configured to pull)
  ## see for details

## The Docker container is stateless; all data is stored in /shared
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## Plugins go here
## see for details
    - exec:
        cd: $home/plugins
          - git clone

## Any custom commands to run after building
  - exec: echo "Beginning of custom commands"
  ## If you want to set the 'From' email address for your first registration, uncomment and change:
  ## After getting the first signup email, re-comment the line. It only needs to run once.
  #- exec: rails r "SiteSetting.notification_email=''"
  - exec: echo "End of custom commands"

Nginx config

# Set discourse upstream paths
upstream discourse {
    server ****.com:8089;

# Rewrite http requests to https
server {
    if ($host = ***.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80 default_server;
	server_name ***.com;
    rewrite ^ https://***.com$request_uri? permanent;


server {

    listen 443 ssl http2;
    # listen on IPv6 as well
    listen [::]:443 ssl http2;
	server_name ***.com;    
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off; 
    keepalive_timeout 65;

    # maximum file upload size (keep up to date when changing the corresponding Discourse)
    client_max_body_size 100m;

    # Pass everything to Discourse
    location / {
        proxy_set_header    Host                 $http_host;
        proxy_set_header    X-Real-IP            $remote_addr;
        proxy_set_header    X-Forwarded-For      $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto    $scheme;
        # Discourse unix socket
        proxy_pass http://discourse;        

    ssl_certificate /etc/letsencrypt/live/***.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/***.com/privkey.pem; # managed by Certbot

Use this code for active conf.d/discourse.conf

sudo ln -s /etc/nginx/sites-available/rt /etc/nginx/sites-enabled/