Running other websites on the same machine as Discourse

If you want to run other websites on the same machine as Discourse, you need to set up an extra NGINX or HAProxy proxy in front of the Docker container.

NOTE: This is for advanced admins

This guide assumes you already have Discourse working - if you don’t, it may be hard to tell whether or not the configuration is working.

You cannot use ./discourse-setup to set up Discourse if another server is using port 80 or 443. You will need to copy and edit samples/standalone.yml with your favorite text editor.

Install nginx outside the container

First, make sure the container is not running:

cd /var/discourse
./launcher stop app

Then install nginx:

sudo apt-get update && sudo apt-get install nginx

Change the container definition

This is where we change how Discourse actually gets set up. We don’t want the container listening on ports - instead, we’ll tell it to listen on a special file.

Change your /var/discourse/containers/app.yml to look like this:

# base templates used; can cut down to include less functionality per container templates:
  # - "templates/cron.template.yml" # cron is now included in base image
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/sshd.template.yml"
  - "templates/web.template.yml"
  # - "templates/web.ssl.template.yml" # remove - https will be handled by outer nginx
  # - "templates/web.letsencrypt.ssl.template.yml" # remove -- https will be handled by outer nginx
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"  # <-- Added

Be sure to remove or comment out the exposed ports by putting a # in front.

# which ports to expose?
# expose: comment out entire section by putting a # in front of each line
# - "80:80"   # http
# - "443:443" # https

If you are using some other reverse proxy that cannot use a web socket, you can instead expose a different port in the section above like - 8080:80.

Create an NGINX ‘site’ for the outer nginx

For an HTTPS site, put this in /etc/nginx/sites-enabled/discourse.conf, making sure to change the server_name:

server {
    listen 80; listen [::]:80;
    server_name forum.example.com;  # <-- change this

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name forum.example.com;  # <-- change this

    ssl_certificate      /var/discourse/shared/standalone/ssl/ssl.crt;
    ssl_certificate_key  /var/discourse/shared/standalone/ssl/ssl.key;
    ssl_dhparam          /var/discourse/shared/standalone/ssl/dhparams.pem;
    ssl_session_tickets off;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA;

    http2_idle_timeout 5m; # up from 3m default
    client_max_body_size 0;

    location / {
        proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

For an HTTP-only site (not recommended, but left here for historical purposes):

server {
	listen 80; listen [::]:80;
	server_name forum.example.com;  # <-- change this

	client_max_body_size 0;

	location / {
		proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
		proxy_set_header Host $http_host;
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Real-IP $remote_addr;
	}
}

Make sure that the default site is either disabled or has the correct server_name set.

Then, in a shell:

# Make sure that Discourse isn't running
/var/discourse/launcher stop app || true

# test configuration
sudo nginx -t
# Important: If nginx -t comes back with an error, correct the config before reloading!
sudo service nginx reload

# Rebuild the container to apply changes
/var/discourse/launcher rebuild app

Create your other sites

You’re done with the Discourse section!

Make other NGINX “sites”, then link and enable them, as in the last step above.

Tips

  • sudo netstat -tulpn : This will tell you what ports are being used
  • /var/log/nginx/error.log : Is the location of the nginx log on ubuntu. This will tell you what the error is when you get a 502 Bad Gateway error.
110 Likes
Port already in use, what to do?
Best way to install Discourse on my server?
Multisite configuration with Docker
Installing Discourse Under Nginx
Nginx configuration for discourse
[Solved] Help setting other websites on the same DigitalOcean droplet
Discourse + Nginx reverse Proxy
Using Multiple Websites with Nginx + Discourse
Setting up nginx for use with Discourse
Nginx + discourse
Discourse not working through separate nginx docker
Faster rebuilds?
Can't make Discourse accessible through Apache
Adding an offline page when rebuilding
Using Discourse With Other Sites on Same Droplet
A better "site not available" page
Wordpress and discourse on the same server
Disable direct access with port (nginx)
Nginx configuration for discourse
Wordpress and Discourse in a single droplet
NGINX proxy in front of the Docker container.errors
Problem with discourse in a subfolder
Discourse doesn't deliver webpages, fresh install on Linode Ubuntu 14.04
Broken images inside posts
Launcher rebuild app failing: repository name must be lowercase [SOLVED]
Port already in use, what to do?
Nginx configuration for discourse
NGINX proxy in front of the Docker container.errors
403 forbidden after installation
Setting up Discourse on a VPS with other sites
Starting a second Discourse forum on the same VPS
Custom Intro page for discourse
Installing docker on an already running web server?
Bad gateway when trying to use SSL
Multiple container setup problems
Multiple discourse installation on single droplet
Which performance is better if discourse or phpbb hosts on a same vps?
Error while doing discourse setup
Run other php script
Where to put custom content?
Domain names pointing with shared AWS instance
How to install discourse on hosting?
Can not visit forum.abc.com again after port change to container/app/yml
Error connecting to Redis
Advice on integrating into a Docker-Compose setup?
Installation on v-server as a subfolder with other services in subfolders using apache
Installation on v-server as a subfolder with other services in subfolders using apache
Installation on v-server as a subfolder with other services in subfolders using apache
Wanting to run Discourse alongside apache
Install Order on CentOS
Add subdomain exception to nginx to host another site on same host
Docker0 Missing (OpenVZ)
[SOLVED] How do you install Discourse and Ghost on the same server using Nginx?
Domain names pointing with shared AWS instance
Nginx rate limiting outside of container - any tips?
Sandbox and test discourse on host?
Best approach for test and production instance of discourse
Mixed content for icon using http on https setup
How do I do what I did in htaccess in nginx?
How do I do what I did in htaccess in nginx?
How Can I set up discourse on my hosting?
CDN causes white screen
[SOLVED] How do you install Discourse and Ghost on the same server using Nginx?
Using a certificate when Discourse is installed behind a reverse proxy
Can I run wordpress in the same VPS with a second IP?
ERR_SSL_PROTOCOL_ERROR with Letsencrypt
Using a certificate when Discourse is installed behind a reverse proxy
Generals Subfolder recommendations and tips
Installing discourse with nginx already running?
Wordpress + Discourse using Digital Ocean and Serverpilot
Unsure About How to Start a Website Project
Install on vps problem?
Install discourse alongside WordPress in a dedicated server
Installation Help required
Installed OK. Working fine for one day. Suddenly stopped working
SSL received a record that exceeded the maximum permissible length
Changing Max Attachment Size
Moved from PluXml and phpBB to Wordpress and Discourse, my all-new experience :tada:
Discourse site loads via IP but via domain only header
Configuring Website with Discourse and Wordpress?!
Looking for a tutorial on discourse/wordpress 1 machine install
Blank pages, 500 errors and no logs
Remote users IPV6 address shows as localhost
Not able to send email Net::ReadTimeout
Adding an offline page when rebuilding
[SOLVED] 502 Gateway Error on Nginx?
URLs for avatars & emoticons adding :80 to the end of the domain
OpenID Connect extension not creating new Discourse users
Installed discourse on a server that already runs a webserver, domain just redirects to subdomain
Installation fails on my Debian server
After upgrade, Connection refused while connecting to upstream
Discourse new Base Image
FAILED to rebuild discourse -- OpenURI::HTTPError: 401 Error: Invalid license key
How to install Discourse on Plesk?
Direct-delivery incoming email for self-hosted sites
Discourse with Traefik 2.0
How to add a subdomain on a different droplet/server?
More than one site as different container running on the same do server
Can I have two domains pointing towards the same IP address without redirect?
Postgres already running
Multiple subdomain instances of Discourse at the same domain?
Install Discourse on a droplet with WordPress served by Apache?
OpenID Connect Authentication Plugin
How to collect all static assets (js, css) used by discourse
Avatar, Site Logos, and Cert Errors
Runing Discourse Along a Wordpress Website
Discourse and WordPress installation on one ubuntu desktop
How do I rebuild app using the host network rather than the docker network?
Can I Host More Than 1 Thing On My VPS?
Discourse won't start because port 80 is in use
How to make Discourse coexist with nginx on a Debian 9 server?
Is it possible the certbot inside the container can affect new certbot outside it?
Prometheus exporter plugin for Discourse
Discourse + web server. Possible or better to avoid?
Help for installing Discourse alongside Apache2
502 Gateway Error: Discourse Installation with SSL form Cloudflare
Installation of Wordpress and Discourse same domain same server?
Multiple subdomain instances of Discourse at the same domain?
I can't set nginx: [emerg] cannot load certificate "/var/discourse/shared/standalone/ssl/ssl.crt": BIO_new_file() failed
Discourse with other websites, SMTP issue: End of file reached
Error: Slow down, too Many Requests from this IP Address
Upload problem with https(or http2) and s3
Links in emails are using http instead of https
Google auth error
Anything else on same Digital Ocean droplet as Discourse?
Global rate limits and throttling in Discourse
Multiple discourse installation on single droplet
Checking domain name fails behind NGINX proxy
OpenID Connect Authentication Plugin
Changing force_https does not update the url of uploaded images causing a mixed content warning
Running Discourse alongside LAMP
429 error when opening multiple topics
What is the best way to install Discourse Forum on a Proxmox VM and expose it on the internet
Multisite Configuration with Let's Encrypt and no reverse proxy
WARNING: Port 443 of computer does not appear to be accessible using hostname
Address already in use during setup
Setting up Discourse on a machine with existing Nginx
Installing discourse with http only (no https)
Multisite Dockerfile issues
Nginx error 502
Nginx error 502
IP Lookup is Pulling CDN's X-Forwarded-For
Not getting Discourse Home page
Installing Discourse to Debian 10.5
Discourse overloaded real traffic or DDOS? 100% CPU usage despite of decent traffic and high specs server
Setting up Discourse on a machine with existing Nginx
Multisite configuration with Docker
Multiple Standalone containers instead of multisite?
Discourse socketed: Nginx in front of discourse: no IP adresses
Address already in use during setup
How should I enable letsencrypt while discourse is beside other websites
How to Docker+Discourse and Apache too?
Help understanding the structure of using discourse + static website
Discourse not showing up at specified hostname
Success - New Multisite Install on Dedicated server using ServerPilot, Nginx and Apache
Error in installing
Install Problem on a Custom Server with Nginx
Regenerating LetsEncrypt keys from behind nginx
[Solved] Dev instance with nginx: topic preview not working
Installation on Ubuntu 14.04 LTS + Apache 2 + Plesk 12
Can't get Discourse to work on different ports
Webhooks/Sidekiq issue on dev instance
Discouse passes localhost uri as oauth redirect_uri
What is the recommended practice for WordPress and Discourse on the same server?
Redirecting old forum URLs to new Discourse URLs
Nginx + discourse
Discourse with zpanel
Backup uploads terribly slow
How to install wordpress in the Secondary directory?
How should I enable letsencrypt while discourse is beside other websites
Moving to subdomain
Deploying discourse on server already running another rails app
How do I install multiple discourse forums on a root server?
How to upload files in root directory?
WordPress, Discourse and Local SMTP Server
Default app.yml file anywhere?
Oauth2 redirect_uri uses HTTP when my forum is using HTTPS
502 Bad gateway error after switching to SSL
Wrong sending domain used
Wrong sending domain used
Wordpress integration with mini forums
How can one install a panel with discourse?
Adding an offline page when rebuilding
Accessing to the database from outside the container
How to change the port from docker - help
Discourse stopped working after installing Plesk
Discourse blocking with password reset
Difference between socket- and port-based connection to outer NGINX?
Nginx configuration for discourse
Custom Layouts Plugin
Setting up HTTPS support with Let's Encrypt
Setting up HTTPS support with Let's Encrypt
Setting up Discourse on a VPS with other sites
Put forum under sub-domain and have website
Can Discourse ship frequent Docker images that do not need to be bootstrapped?
Can Discourse ship frequent Docker images that do not need to be bootstrapped?
IP Spoofing Attack

The guide in the first post is great and, on the whole, still works just fine :sunny:

There are three things worth noting:

  1. I initially missed some of the app.yml changes. There are 3 things you need to change in your app.yml. If you miss any of these things it won’t work:

    1. Comment out all ssl templates in the templates. If you are using letsencrypt you will have two:
      # - "templates/web.ssl.template.yml"
      # - "templates/web.letsencrypt.ssl.template.yml"
      
    2. Add a socket template:
      - "templates/web.socketed.template.yml" 
      
    3. Comment out all exposed ports:
      # - "80:80"   # http
      # - "443:443" # https
      
  2. As others mentioned, I had to change the ssl cert and key names in the discourse.conf:

    ssl_certificate      /var/discourse/shared/standalone/ssl/discourse.angusmcleod.com.au.cer;
    ssl_certificate_key  /var/discourse/shared/standalone/ssl/discourse.angusmcleod.com.au.key;
    
  3. Turns out my site didn’t have a dhparams.pem key (dh stands for Diffie Hellman, there’s some good explanations of what this is here). You can generate this yourself:

    openssl dhparam -out /var/discourse/shared/standalone/ssl/dhparams.pem 2048
    

Some other things you may find useful:

  • sudo netstat -tulpn: This will tell you what ports are being used

  • /var/log/nginx/error.log: Is the location of the nginx log on ubuntu. This will tell you what the error is when you get a 502 Bad Gateway error.

  • You may finish a ./launcher rebuild app, excitedly go to your domain to see if it worked and be greeted with a depressing 502 Bad Gateway error. Before giving up in frustration, try restarting nginx one more time:

    sudo service nginx restart

    This clinched it for me.

Now my sandbox is using nginx outside the container (although I haven’t added the extra website yet).

34 Likes

Hi,

I am just doing a fresh install of Discourse.
I am very surprised that :

  • The setup script does not simply allow to setup on a different port, disabling SSL support, and then it’s up to us to do reverse proxy as we want (Apache, Nging) : It seems such a common setup, it would save hundreds of hours of life to humanity
  • I am even more surprised that the setup redirects us to an outdated forum topic, in which we need to dig for the proper information, instead of a proper documentation page, kept up to date.

Thanks guys anyway for your work, but I think there is room for improvement here.
Cheers

9 Likes

Just wanted to share how I accomplished this, it was a little easier than I first thought.

From an already running machine, with Caddy acting as a reverse proxy for a number of existing Docker containers already.

  1. Clone discourse as per the official instructions
  2. Copy /var/discourse/samples/standalone.yml -> /var/discourse/containers/app.yml
  3. Fill in SMTP settings and website address
  4. Comment out 443:443 from the expose: section
  5. Replace 80:80 -> 3001:80, 3001 being the port I’m serving via Caddy
  6. Run ./launcher rebuild app
  7. Done

Whop!

8 Likes

@TheBestPessimist I swapped the order of the HTTP/HTTPS sections, it works better than adding a “don’t do this!” notice :slight_smile:

6 Likes

I would recommend just doing double NGINX, it is far more complex to set it up direct and has virtually no performance cost. Getting it right would be very hard cause we serve some files direct and have a layer of caching that is tricky to configure.

Note, customizing NGINX is easy with mixin templates have a look at the various example templates here: https://github.com/discourse/discourse_docker/tree/master/templates

6 Likes

PSA: If you want to allow uploads larger than 1MB to your site, you’ll also have to set client_max_body_size 100M in any nginx config that sits in front of your site.

For reference, here’s the full nginx config I use:

nginx config for other sites on same host
server {
    if ($host = discourse.mysite.org) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = backupname.mysite.org) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80; listen [::]:80;
    server_name discourse.mysite.org;
    server_name backupname.mysite.org;

    location / {
        proxy_pass http://unix:/var/discourse/shared/mysite/nginx.http.sock:;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

# nginx 1.14.1 | intermediate profile | OpenSSL 1.1.0f | link

server {
    listen 443 ssl http2;  listen [::]:443 ssl http2;
    server_name discourse.mysite.org;
    server_name backupname.mysite.org;

    # from discourse examples
    http2_idle_timeout 5m; # up from 3m default
    client_max_body_size 50M; # allow 50M uploads

    location / {
        proxy_pass http://unix:/var/discourse/shared/mysite/nginx.http.sock:;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
    }
    ssl_certificate /etc/letsencrypt/live/backupname.mysite.org/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/backupname.mysite.org/privkey.pem; # managed by Certbot

    ###### https://mozilla.github.io/server-side-tls/ssl-config-generator/ ####

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # modern configuration. tweak to your needs.
    ssl_protocols TLSv1.2;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
    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 /etc/letsencrypt/live/backupname.mysite.org/chain.pem;

    resolver 1.1.1.1;

}
9 Likes

Are there any reasons not to use NGINX Proxy Manager instead of doing this manually?

1 Like

Discussing which reverse proxy you should use is clearly beyond the supported area. :wink: But after looking at NGINX Proxy Manager for 12 seconds or more, I’d think that it’ll do fine. There’s another automagic NGINX proxy thing that I’ve seen discussed before as well, but with my extensive knowledge of NGINX Proxy Manager, I think it might be better. I might take a look at it, as I’m growing disenchanted with Traefik.

EDIT: Now I’ve looked at it for 3 minutes. I prefer tools that facilitate automation, so Traefik and Docker Hub (I found it) that allow you to add some labels or environment variables to the docker container so that no clicking in a web interface is required are more what I want, and it looks like to make that thing work you have to use the web interface or contrive to update the database manually with hosts that you want to add. But if you’re willing to poke and click on a web interface like a Normal Person (rather than a system administrator “person”), then it looks like that thing could be just what you’re looking for.

3 Likes

I’m already using it. I’ve had it on my home server for a while and when I migrated my discourse instance to a new cloud server, I realized that I’ve forgotten most of the NGINX reverse proxy setup that I did 4 years ago on the old server so I thought: why not do it with NPM? To my surprise I found that it has been mentioned rather rarely here on meta so I started wondering whether there are sone downsides that I might have missed…

One obvious downside is, of course that it depends on the container being up and running. Having your reverse proxy run directly on the host might be more stable.

It also took me some trial and error to figure out where I put the location of the web socket

My main remaining challenge is that discourse uses the default bridge network which means NPM should also be on that network but that entails some limitations (no internal DNS).

1 Like

Increasingly folks would rather trust anything running in a container than something running directly on the host. It’s much easier to have the configuration all in one place should you need to move to another host, upgrade, or whatever else.

2 Likes

That’s exactly why I’m containerizing everything for which a stable image exists (I’m still far from building my own images).

After I posted my post above, I actually started wondering whether a container necessarily is more stable, given that, well, it’s containerized, i.e. shielded, to a certain extent, from both the host and other containers. So misconfiguration or bugs may actually be less harmful than if the same happened directly on the host.

I also agree that its a huge advantage that all config is in one place so that it can easily be removed and you know it’s really gone. No hidden config file leading to strange behaviour, no forgotten demon disturbing other processes.

2 Likes

angus’s instructions still work; I have a question, though: will LetsEncrypt still work automatically with this after 3 months when it’s time to renew the certificates? (I assume/hope the answer is yes since the host nginx reads them directly from /var/discourse/shared/standalone?)

1 Like

@Godmar_Back I ran into this problem right now, and my certificate was NOT renewed.

@angus - since we use the letsencrypt certs created by discourse, but commented out the relevant parts in app.yml, I guess the update script is no longer being started. Did you find a solution for that?

1 Like

I tried entering the app and

 "/shared/letsencrypt"/acme.sh --cron --home "/shared/letsencrypt" --force

which kind of showed me at least a problem.

my.site.com:Verify error:Fetching https://my.seite.com/.well-known/acme-challenge/XXXXXXXedited-out-XXXXXXXXXXXXXXXXXXXX: Error getting validation data
[Mi 25. Aug 10:41:37 UTC 2021] Please check log file for more details: /shared/letsencrypt/acme.sh.log

Acme.sh.log pretty much repeats the error message. But it also tells me that all config files are empty.

On the otherhand acme.sh updated itself, so I assume it has not been started in the background for a while, probably since I changed to the outer nginx.

1 Like

You’ll need to handle those outside the container. Follow whatever guide is for let’s encrypt and nginx.

1 Like

I’ve tried installing Nginx Proxy Manager but I can’t understand how to “link” (proxy? sorry but I’m quite new to system management) the Discourse container in order to see Discourse when I access from web.
Can you give same hints? Should the Discourse container expose 80 and 443 ports or not, like this forum topic suggests?

1 Like

Indeed, this required a bit of trial and error, but I got it to work as follows (no guarantee that this is the best way of doing it - in fact, I know that there must be a better way - so corrections and improvements are very welcome):

To start with, there are two ways of accessing your discourse instance: 1. by exposing a port, 2. via websocket. I believe I learned somewhere on this forum that the websocket is faster/ more efficient so this is what I’m using, but exposing a port should be a lot easier, so if you can’t get the socket to work, try exposing a port (more on that below).

So let’s assume you have completed the 30-minute standard installation and let’s assume that you didn’t let discourse acquire a lets encrypt cert yet - because you don’t need it when using a reverse proxy. NPM will take care of that. It doesn’t matter, though, if you already have certificate. NPM will simply get a new one.

1. Install NPM

Next step is to install NPM so that you will have two more docker containers running (NPM and its database container).

Next is the tricky part that you’re asking about.

The first obstacle is that discourse runs on the default docker bridge network while NPM by default runs on a default “user created network” (called npm_default in my case) which means that NPM can’t see discourse. :cry:

2. Bring all containers into the default bridge network

So as long as I don’t know if and how discourse can be moved to a custom network, we have to move NPM into the default bridge network. We can do this by adding network_mode: bridge to both NPM containers in our docker compose file.

3. Use IP address instead of service name

The next problem is that the standard docker compose file won’t work any more if you just move it to the bridge network. NPM won’t be able to find its database container anymore. This is because internal DNS resolution for service names (which the docker-compose file relies on) is only available on user created networks, not on the default docker networks. So we have to resort to hard coded IP addresses (which is why this is definitely not the optimal solution because it will break if your container IPs change). So you need to start the container even though you know it wont work, note the IP of the NPM database container, and replace DB_MYSQL_HOST: "db" in your docker compose file with DB_MYSQL_HOST: "<db_container_IP>".

So now all containers should be on the default bridge network so that NPM can see both discourse and its database.

4. Make discourse accessible

But “seeing” discourse and being able to access it is not the same thing. So you need to make sure that discourse will accept whatever traffic NPM forwards to it. If you don’t care about using the websocket, I suppose you can just point NPM to port 80 (not 443) of your discourse container-IP, like this:

Havn’t tested this, though. As I mentioned, I’m using the websocket setup, which requires some further steps. Note that the hostname/IP and port above will be ignored when you use the websocket.

5. Configure app.yml to use websocket

This is explained in the OP, so I won’t go into this.

6. Mount websocket in NPM container

We need to give NPM access to the websocket by mounting it as a volume: - /var/discourse/shared/standalone/nginx.http.sock:/var/discourse/shared/standalone/nginx.http.sock. This is the final change to the default NPM docker compose file, so here is the final version that works for me:

version: '3'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    network_mode: bridge
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    environment:
      DB_MYSQL_HOST: "172.17.0.6"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm"
      DB_MYSQL_PASSWORD: "my-super-safe-pwd"
      DB_MYSQL_NAME: "npm"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
      - /var/discourse/shared/standalone/nginx.http.sock:/var/discourse/shared/standalone/nginx.http.sock
  db:
    image: 'jc21/mariadb-aria:latest'
    restart: unless-stopped
    network_mode: bridge
    environment:
      MYSQL_ROOT_PASSWORD: 'my-super-safe-pwd'
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'npm'
      MYSQL_PASSWORD: 'my-super-safe-pwd'
    volumes:
      - ./data/mysql:/var/lib/mysql

7. Configure NPM to use the websocket

Last step: tell NPM to use the websocket. As far as I remember it wasn’t enough to just turn on “Websockets support” so I copied the NGINX location from the OP to the “Advanced” tab, like this:

I did not get this to work under the “Custom locations” tab.

8. Don’t forget to activate SSL

I didn’t mention the SSL configuration in NPM because it seems pretty self-evident and I don’t think it matters at which point in the process you activate it. So if you haven’t done it yet, this is what mine looks like:

image


9. Final disclaimer

As I wrote this post, it suddenly occurred to me that NPM and discourse probably don’t even need to be on the same docker network when we use the websocket. I don’t have time to check this at the moment, but if this is true, then you can just forget about steps 2, 3 and 4 above and it should work.

This is the most fascinating aspect of support forums: doing a good job in describing your problem often leads you to the solution without even posting your question. And in this case, I was answering someone else’s question but also might have found the answer to my own. :smiley:

2 Likes

Thanks a lot for the great and articulated answer! I will try asap you hints. By the way, the ip to put in NPM are those of the server (i.e. the external ip to reach the server) or the Docker internal (I noticed Docker uses 172… for containers)?
Sorry if my questions seems trivial, I don’t know well net matters.

1 Like

I recommend that you use the websocket. Then you can put anything as the IP because it won’t be used. Otherwise it is the internal IP of the container, not the public IP of the host.

BTW: Looks like your reply-by-email wasn’t rendered correctly. Maybe you want to edit (shorten) your post above?

2 Likes