Discourse not loading with Apache and proxy redirect

I’ve spent the last few days trying to find a way to pair Discourse with Apache on the same DigitalOcean droplet and the tutorials available here on Discourse are either outdated or not applicable to me. One tutorial uses CentOS and HAProxy (I use Ubuntu) and the other uses Nginx (I use Apache).

I came across a comment on a DigitalOcean thread and followed the instructions there on a test droplet: Install Discourse on a droplet with WordPress served by Apache ? | DigitalOcean

Everything seems to have gone smoothly, including the Let’s Encrypt SSL. My homepage, a few static HTML documents, works just fine. Discourse, despite installing without errors, does not show. When navigating to community.mysite.com, I just see my homepage with an unsecured HTTPS in the URL. Yes, my DNS settings are accurate and pointing to the correct server.

This is my mysite.com.conf:

<VirtualHost *:80>
	ServerAdmin webmaster@localhost
	ServerName mysite.com
	ServerAlias www.mysite.com
	DocumentRoot /var/www/mysite.com
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
RewriteEngine on
RewriteCond %{SERVER_NAME} =www.mysite.com [OR]
RewriteCond %{SERVER_NAME} =mysite.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

This is my mysite.com-le-ssl.conf

<IfModule mod_ssl.c>
<VirtualHost *:443>
	ServerAdmin webmaster@localhost
	ServerName mysite.com
	ServerAlias www.mysite.com
	DocumentRoot /var/www/mysite.com
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/mysite.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mysite.com/privkey.pem
</VirtualHost>
</IfModule>

This is my community.mysite.com.conf:

<VirtualHost *:80>
  ServerName community.mysite.com
  ServerAlias www.community.mysite.com

  <IfModule proxy_module>
    ProxyPreserveHost on
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
  </IfModule>
</VirtualHost>

And finally, here is my app.yml (edited of course):

## 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
##
## BE *VERY* CAREFUL WHEN EDITING!
## YAML FILES ARE SUPER SUPER SENSITIVE TO MISTAKES IN WHITESPACE OR ALIGNMENT!
## visit http://www.yamllint.com/ to validate this file as needed

templates:
  - "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 https://meta.discourse.org/t/17247 for details
expose:
      - "8080:80"   # fwd host port 8080   to container port 80 (http)
      - "8443:443"   # fwd host port 8443 to container port 443 (http)

params:
  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: "128MB"

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

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en

  ## 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
  UNICORN_WORKERS: 2

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

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

  ## TODO: List of comma delimited emails that will be made admin and developer
  ## on initial signup example 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'myemail'

  ## 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_ADDRESS: smtp.mailgun.org
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: my-smtp-username
  DISCOURSE_SMTP_PASSWORD: "my-smtp-password"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)

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

  ## The http or https CDN address for this Discourse instance (configured to pull)
  ## see https://meta.discourse.org/t/14857 for details
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

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

## Plugins go here
## see https://meta.discourse.org/t/19157 for details
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Any custom commands to run after building
run:
  - 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='info@unconfigured.discourse.org'"
  - exec: echo "End of custom commands"

Can anyone tell me where I went wrong with this or what I might be missing? Thanks!

You’ll have a much better luck trying nginx as a reverse proxy.

If your system allows, consider using nginx instead of apache2

2 Likes

so community does not redirect to your domain but points to your IP, is the symlink present in /etc/apache2/sites-enabled/ ?

Is proxy_module loaded ?
apache2ctl -M

1 Like

No symlink.

Yes. Fortunately, that’s one of the steps in the tutorial I linked to.

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_balancer
a2enmod lbmethod_byrequests

the step

a2ensite community.yoursite.com

might not have gone well then, might need a sudo.

sudo ln -s /etc/apache2/sites-available/community.yoursite.com.conf /etc/apache2/sites-enabled/

should also do it.
then

sudo apachectl configtest

and fingers cross

sudo systemctl restart apache2

Dear @OrbitStorm,

I took a quick look at your Apache2 virtual hosts configuration for you and your yml file for Discourse and they do not appear to be correctly configured.

Here are some hints:

First of all, when you run Discourse behind a reverse proxy, you do not enable SSL LETSENCRYPT in the Discourse yml configuration (see working example below). Discourse only needs a single port to communicate with the reverse proxy and that is not an SSL encrypted connection.

Second, if you look at your main virtual host configuration, which is the port 443 on the reverse proxy:

<IfModule mod_ssl.c>
<VirtualHost *:443>
	ServerAdmin webmaster@localhost
	ServerName mysite.com
	ServerAlias www.mysite.com
	DocumentRoot /var/www/mysite.com
	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

       Include /etc/letsencrypt/options-ssl-apache.conf
       SSLCertificateFile /etc/letsencrypt/live/mysite.com/fullchain.pem
       SSLCertificateKeyFile /etc/letsencrypt/live/mysite.com/privkey.pem
</VirtualHost>
</IfModule>

That configuration above is missing all necessary reverse proxy information (see attached working configurations below).

Here is a working configuration for you, which is basically the same as outlined in the various tutorials on meta (well documented on this site in other posts, so this is basically duplicating other documentation here at meta):

<VirtualHost *:80>
        ServerName discourse.your-great-web-site.com
        ServerAdmin webmaster@localhost
        ProxyPreserveHost On
        ErrorLog ${APACHE_LOG_DIR}/discourse_errors.log
        CustomLog ${APACHE_LOG_DIR}/discourse.log combined
        ModPagespeed Off
        RewriteEngine on
        RewriteCond %{SERVER_NAME} =discourse.your-great-web-site.com
        RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Note that in the port 80 configuration, there is the main required directives are the ServerName, RewriteEngine and rewrite rules to redirect to port 443.

Also, if you are running Apache2 mod_pagespeed you should disable as I have not yet got mod_pagespeed working with Discourse (and see no reason to do so either).

Here is the main config, where the “real-work” is done:

<IfModule mod_ssl.c>
<VirtualHost *:443>
        ServerName discourse.your-great-web-site.com
        ServerAdmin webmaster@localhost
        SSLProxyEngine on      # enable this only after let's encrypt is configured and working
  	    RewriteEngine on
  	    ProxyPreserveHost On
  	    ProxyRequests Off
  	    RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
 	    RequestHeader set X-Real-IP expr=%{REMOTE_ADDR}

        #ProxyPass / http://127.0.0.1:8888/           # we do not use port, we use your-great-web-site sockets
        #ProxyPassReverse / http://127.0.0.1:8888/    # we do not use port, we use your-great-web-site sockets
        ProxyPass / your-great-web-site:/var/discourse/shared/socket-only/nginx.http.sock|http://localhost/
        ProxyPassReverse  / your-great-web-site:/var/discourse/shared/socket-only/nginx.http.sock|http://localhost/

        ErrorLog ${APACHE_LOG_DIR}/discourse_errors_ssl.log
        #CustomLog ${APACHE_LOG_DIR}/discourse_ssl.log combined   #access log disabled for production

        ModPagespeed Off
        SSLCertificateFile /etc/letsencrypt/live/discourse.your-great-web-site.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/discourse.your-great-web-site.com/privkey.pem
        Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>

All our discourse configurations use unix domain sockets, so you will need to change the configuration to match your desired configuration.

The main point to understand (in summary), is that you should disable LETSENCRYPT in your Discourse build (yml) configuration and only expose a single entry point into Discourse, in our case a unix domain socket, in your case a single TCP/IP socket.

Then you reverse proxy to that entry point from the virtual host file for port 443 (not from the port 80 virtual host). The port 80 virtual host simply redirects to 443. Your 443 SSL is all done by LETSENCRYPT in the reverse proxy. There is no SSL required in your Discourse yml file (see working example below).

Here is one of our working yml files (for the configuration above) for you to review:

/var/discourse/containers$ cat socket-only.yml
# IMPORTANT: SET A SECRET PASSWORD in Postgres for the Discourse User
# TODO: change SOME_SECRET in this template

templates:
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
#  - "templates/sshd.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 https://meta.discourse.org/t/17247 for details
expose:
#  - "80:80"   # http
#  - "443:443" # https

# Use 'links' key to link containers together, aka use Docker --link flag.
links:
  - link:
      name: data
      alias: data

# any extra arguments for Docker?
# docker_args:

params:
  ## Which Git revision should this container use? (default: tests-passed)
  #version: latest
  db_shared_buffers: "4GB"

env:
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8

  # DISCOURSE_DEFAULT_LOCALE: en

  ## 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
  #UNICORN_WORKERS: 3
  UNICORN_WORKERS: 8

  ## TODO: The domain name this Discourse instance will respond to
  DISCOURSE_HOSTNAME: 'discourse.your-great-web-site.com'

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

  ## TODO: List of comma delimited emails that will be made admin and developer
  ## on initial signup example 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'tim@discourse.your-great-web-site.com'

  ## 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_ADDRESS: smtp.gmail.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: not_for_reply@discourse.your-great-web-site.com
  DISCOURSE_SMTP_PASSWORD: my_super_secret_cool_password
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optional, default true)

  ## If you added the Lets Encrypt template, uncomment below to get a free SSL certificate
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

  ## TODO: configure connectivity to the databases
  DISCOURSE_DB_SOCKET: ''
  #DISCOURSE_DB_USERNAME: discourse
  DISCOURSE_DB_PASSWORD: another_super_secret_cool_password
  DISCOURSE_DB_HOST: data
  DISCOURSE_REDIS_HOST: data

  DISCOURSE_MAXMIND_LICENSE_KEY: my_max_mind_key
  ## The http or https CDN address for this Discourse instance (configured to pull)
  ## see https://meta.discourse.org/t/14857 for details
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com

volumes:
  - volume:
      host: /var/discourse/shared/socket-only
      guest: /shared
  - volume:
      host: /var/discourse/shared/socket-only/log/var-log
      guest: /var/log

## Plugins go here
## see https://meta.discourse.org/t/19157 for details
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-bbcode.git
          - git clone https://github.com/discourse/discourse-sitemap.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-whos-online.git
          - git clone https://github.com/unixneo/legacy-info.git

## Remember, this is YAML syntax - you can only have one block with a name
run:
  - exec: echo "Beginning of custom commands"

  ## If you want to configure password login for root, uncomment and change:
  ## Use only one of the following lines:
  #- exec: /usr/sbin/usermod -p 'PASSWORD_HASH' root
  #- exec: /usr/sbin/usermod -p "$(mkpasswd -m sha-256 'RAW_PASSWORD')" root

  ## If you want to authorized additional users, uncomment and change:
  #- exec: ssh-import-id username
  #- exec: ssh-import-id anotherusername

  - exec: echo "End of custom commands"
  #- exec: awk -F\# '{print $1;}' ~/.ssh/authorized_keys | awk 'BEGIN { print "Authorized SSH keys for this container:"; } NF>=2 {print $NF;}'

It’s really easy once you understand the basics; and understanding the basics does help quite a lot :slight_smile:

Please keep in mind that in our Discourse configurations, we do not run a single container (actually, we rarely run in single container mode), so our yml file will not work in a single container (standalone) configuration. I am providing it to you for reference to help you, by showing you what a fully working configuration behind Apache2 looks like.

We run Discourse behind both Apache2 and nginx reverse proxies. In fact, we only use reverse proxies for many reasons. One reason is that we can use the reverse proxy to filter bad bots, etc (a totally different subject).

There is nothing difficult about running Dockerized Discourse (as many sites as you want, one or 100) behind a reverse proxy on a site with Apache2; but it does help to understand the basic concepts.

I hope providing you both the basic concepts and working example configuration files help you move forward and get Discourse up and running.

Best Regards…

1 Like

@neounix I appreciate that incredibly in-depth response but it seems most of the issues I’m running into are a result of not being able to find a suitable updated tutorial. Several tutorials here say to enable SSL for Discourse in the yml file and one such tutorial says that I don’t need to add proxy information to my VirtualHost files because Let’s Encrypt should do it automatically. Your examples are wildly different even from my defaults making them just as confusing as trying to blend a CentOS configuration with Ubuntu.

You’re absolutely right in saying that understanding the basics goes a long way but the problem is the basics aren’t properly covered and when they are, they’re dated from three years ago and without any consideration that someone uses Apache instead of Nginx or Ubuntu instead of CentOS. It’s worth noting too that Discourse is the only reason I have any use for Docker.

After spending four days just trying to get the application installed alongside Apache, I’m done. Free or not, this isn’t worth the headache and I’m not going to continue combing the forums just to keep finding copy/pasted links to the same two outdated and incomplete tutorials. I’ve never had a frustrating experience with simply installing forum software until Discourse. Speaks volumes. XenForo and Invision are two platforms I have immeasurable experience with and they’re a cakewalk to install/use.

I truly do appreciate the effort you put into your response but I’m not going to ask you to do the work for me, nor should I have to if proper documentation existed. It’s simply amazing to me that my specific circumstances, which aren’t so specific to me, have not been highlighted in a new tutorial that would make this process easier for new users.

Whatever the case, best of luck to you.

1 Like

Despite having every intention of moving on from Discourse given the utter lack of current documentation that isn’t hyper-focused on CentOS/Nginx, an incredibly generous and patient individual at DigitalOcean responded to a thread I had created there and after some trial and error, helped put together a simple-to-follow tutorial for prospective Discourse users with the same circumstances as mine.

As a refresher, those circumstances are:

Installing Discourse on the same server as Apache | Using Ubuntu 18.04 | DigitalOcean

credit to Bobbyiliev @ DigitalOcean
Install Discourse on a droplet with WordPress served by Apache ? | DigitalOcean (first answer)

Prerequisites

  • To be on the safe side make sure to backup your Droplet, so that in case anything goes wrong, you could revert back to a working version
  • SSH to your Droplet
  • Apache installed, you can follow the steps on how to do that here:

Step 1 - Install Docker

To install Docker please follow the steps here:

Step 2 - Download Discourse

First, create a directory where you would store your Discourse files:

mkdir /var/discourse

After that clone the official Discourse Docker Image into /var/discourse .

git clone https://github.com/discourse/discourse_docker.git /var/discourse

Step 3 - Configure Discourse to listen on port 8080

We will use the standalone.yml template it includes all of the necessary services like PostgreSQL, Redis and etc.

You copy the sample file with the following command:

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

After that edit the file with your favorite editor. Open the /var/discourse/containers/app.yml and update and the ports on lines 23 and 24:

## which TCP/IP ports should this container expose?
expose:
  - "8080:80"   # fwd host port 8080   to container port 80 (http)
  - "8443:443"   # fwd host port 8443 to container port 443 (http)

Also if you do not yet have an SSL certificate make sure to comment line 16 out:

  #- "templates/web.ssl.template.yml"

Just add the # symbol in front of the - "templates/web.ssl.template.yml line otherwise Discourse would not start.

Step 4 - Setup Discourse

Change directory:

cd /var/discourse

Then, start Discourse (as this is the first time that you are starting the service it would bootstrap the application with the new changes that you have in your app.yml file):

./discourse-setup

Note : Make sure to provide valid Mail server settings as otherwise, the setup might fail.

Step 5 - Setup Apache

In your /etc/apache2/sites-available/ create a new file called forum.example.com.conf and add the following Vhost content:

<VirtualHost *:80>
  ServerName forum.example.com
  ServerAlias www.forum.example.com

  <IfModule proxy_module>
    ProxyPreserveHost on
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/
  </IfModule>
</VirtualHost>
  • Enable the Vhost with the following command:
a2ensite forum.example.com
  • Enable Mod Proxy:
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_balancer
a2enmod lbmethod_byrequests
  • Restart Apache:
systemctl restart apache2

And after that, you would be able to access Discourse directly via your domain name.


Note: 1 When installing Discourse, app.yml may be overwritten and lines 16/17 (SSL) will be uncommented. You’ll need to comment those lines out again then rebuild the app (don’t forget to change directory): ./launcher rebuild app

Note 2: SSL is not enabled for Discourse with this guide. Unsurprisingly, it appears no documentation exists for enabling Let’s Encrypt SSL if you already have it enabled for Apache. If someone happens upon a solution for this, please reach out.

4 Likes