# Discourse with jwilder/nginx-proxy howtos?

**URL:** https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604
**Category:** Self-hosting
**Created:** [August 13, 2018, 1:49pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604 "2018-08-13T13:49:55Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![confused](https://avatars.discourse-cdn.com/v4/letter/c/f08c70/32.png) [@confused](https://meta.discourse.org/u/confused)
#### Post date: [August 13, 2018, 1:49pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/1 "2018-08-13T13:49:55Z")

</div>

Hi All

I would like to deploy Discourse behind nginx-proxy ([GitHub - nginx-proxy/nginx-proxy: Automated Nginx Reverse Proxy for Docker · GitHub](https://github.com/jwilder/nginx-proxy)). This seems to be a pretty popular proxy and I already have it working with other sites and its Letsencrypt companion.

There’s small bits of information scattered all over the forum, but nothing concrete, whether it has been done and detailed ‘how’ it has been done.

Anything that would help would be really appreciated.

Thanks!

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [August 13, 2018, 3:52pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/2 "2018-08-13T15:52:38Z")

</div>

It looks like it should “just work”. You’ll need to change the Discourse port number by editing `app.yml` by hand (and, obviously, don’t enable Let’s Encrypt in `app.yml`).

Give it a try and let us know.

---

<div class="post-metadata">

### Author: ![confused](https://avatars.discourse-cdn.com/v4/letter/c/f08c70/32.png) [@confused](https://meta.discourse.org/u/confused)
#### Post date: [August 14, 2018, 2:36pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/3 "2018-08-14T14:36:36Z")

</div>

Thanks, @pfaffman. I tried that. No luck. I get something like this, when checking my nginx-logs:

> 2018/08/14 14:11:25 [error] 8#8: \*60 no live upstreams while connecting to upstream, client: {my\_client\_ip}, server: {[myapp.mydomain.com](http://myapp.mydomain.com)}, request: “GET / HTTP/1.1”, upstream: “http://{[myapp.mydomain.com](http://myapp.mydomain.com)}”, host: “{[myapp.mydomain.com](http://myapp.mydomain.com)}”

It seems to be a common thing, as far as I can tell, based on pieces of info I could scrounge up.

It’s almost as if all the VIRTUAL\_HOST, \_PORT, etc. are not getting recognised or Discourse is not making it’s port available. However, the companion does generate valid SSL certs, so I am guessing environment variables do get recognised. Thus leaning towards something to do with how ports are exposed via Discourse.

---

<div class="post-metadata">

### Author: ![zerek](https://avatars.discourse-cdn.com/v4/letter/z/f08c70/32.png) [@zerek](https://meta.discourse.org/u/zerek)
#### Post date: [September 21, 2018, 6:38am UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/4 "2018-09-21T06:38:43Z")

</div>

Hi, I get the same troubles as @confused …  
It seems that it doesn’t take care of VIRTUAL\_HOST environment.  
Moreover, why can’t we set the “ports” parameters as exposed by docker-compose ?  
If anyone else has a workaround, that would save a lot of time…  
Thank you.

---

<div class="post-metadata">

### Author: ![thwright](https://avatars.discourse-cdn.com/v4/letter/t/e19b73/32.png) [@thwright](https://meta.discourse.org/u/thwright)
#### Post date: [September 21, 2018, 8:00pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/5 "2018-09-21T20:00:57Z")

</div>

I have shared various files of mine below with personal settings obfuscated. My setup is as follows.

- Two Ubuntu 18.04 servers running Nginx: Server1, Server 2
- Both servers are running Nginx v. 1.14.0 with LetsEncrypt SSL certificates.
- Both servers are using [CSF](https://configserver.com/cp/csf.html) as a software-level firewall.
- The main domain is listed as [domain.com](http://domain.com) and Discourse runs on [www.domain.com](http://www.domain.com).
- All traffic is forced through Server1 which then proxies to Server2.
- Server1 uses a static IP address. Server2 uses a dynamic IP address.
- Discourse utilizes a web socket
- I do have other content running on the server.
- I am not using a CDN. I do have CloudFlare, but it is only being utilized for DNS.

Various of my config files can be condensed, separating them just cleans up the files and allows for code reuse. I do have other config files, most of which are given in the list; I have not included them as they do not affect Discourse’s function (e.g. restrictions.conf).

While this does not use the automated proxy given in the OP, you can dissect the following configuration files for whatever you might need to add (or run it without the automated proxy). I have had to slowly tackle various problems I’ve run into over my time running Discourse; at the given moment, I am not aware of any outstanding errors being recorded to my logs.

I’m happy to provide whatever information I can. I had worked as a Linux Systems Administrator for a couple of years and am happy to provide what knowledge I can.

I hope this helps!

> **Server #1 nginx.conf**
>
> ```
> user www-data;
> worker_processes auto;
> pid /var/run/nginx.pid;
> 
> include /etc/nginx/modules-enabled/50-mod-http-geoip.conf;
> 
> error_log /var/log/nginx/error.log info;
> 
> events {
> worker_connections 1024;
> multi_accept on;
> use epoll;
> }
> 
> worker_rlimit_nofile 30000;
> 
> http {
> 
> map $remote_addr $ip_anonym1 {
> default 0.0.0;
> "~(?P<ip>(\d+)\.(\d+)\.(\d+))\.\d+" $ip;
> "~(?P<ip>[^:]+:[^:]+):" $ip;
> }
> 
> map $remote_addr $ip_anonym2 {
> default .0;
> "~(?P<ip>(\d+)\.(\d+)\.(\d+))\.\d+" .0;
> "~(?P<ip>[^:]+:[^:]+):" ::;
> }
> 
> map $ip_anonym1$ip_anonym2 $ip_anonymized {
> default 0.0.0.0;
> "~(?P<ip>.*)" $ip;
> }
> 
> log_format anonymized '$ip_anonymized - $remote_user [$time_local] '
> '"$request" $status $body_bytes_sent '
> '"$http_referer" "$http_user_agent"';
> 
> server_names_hash_bucket_size 64;
> upstream php-handler {
> server unix:/run/php/php7.2-fpm.sock;
> }
> 
> # Cloudflare IPs
> set_real_ip_from 204.93.240.0/24;
> set_real_ip_from 204.93.177.0/24;
> set_real_ip_from 199.27.128.0/21;
> set_real_ip_from 173.245.48.0/20;
> set_real_ip_from 103.21.244.0/22;
> set_real_ip_from 103.22.200.0/22;
> set_real_ip_from 103.31.4.0/22;
> set_real_ip_from 141.101.64.0/18;
> set_real_ip_from 108.162.192.0/18;
> set_real_ip_from 190.93.240.0/20;
> set_real_ip_from 188.114.96.0/20;
> set_real_ip_from 197.234.240.0/22;
> set_real_ip_from 198.41.128.0/17;
> set_real_ip_from 162.158.0.0/15;
> real_ip_header CF-Connecting-IP;
> 
> include /etc/nginx/mime.types;
> include /etc/nginx/conf.d/optimization.conf;
> default_type application/octet-stream;
> 
> log_format main '$remote_addr - $remote_user [$time_local] "$request" '
> '$status $body_bytes_sent "$http_referer" '
> '"$http_user_agent" "$http_x_forwarded_for" '
> '"$host" sn="$server_name" '
> 'rt=$request_time '
> 'ua="$upstream_addr" us="$upstream_status" '
> 'ut="$upstream_response_time" ul="$upstream_response_length" '
> 'cs=$upstream_cache_status' ;
> 
> access_log /var/log/nginx/access.log main;
> 
> client_max_body_size 30M;
> sendfile on;
> send_timeout 3600;
> tcp_nopush on;
> tcp_nodelay on;
> open_file_cache max=500 inactive=10m;
> open_file_cache_errors on;
> keepalive_timeout 120;
> reset_timedout_connection on;
> server_tokens off;
> resolver_timeout 10s;
> include /etc/nginx/conf.d/php_optimization.conf;
> include /etc/nginx/conf.d/proxy.conf;
> include /etc/nginx/conf.d/gateway.conf;
> include /etc/nginx/conf.d/letsencrypt.conf;
> include /etc/nginx/sites-enabled/*.vhost;
> include /etc/nginx/sites-enabled/*.conf;
> }
> 
> ```

> **Server #1 proxy.conf**
>
> ```
> proxy_set_header Host $host;
> proxy_set_header X-Real-IP $remote_addr;
> proxy_set_header X-Forwarded-Host $host;
> proxy_set_header X-Forwarded-Protocol $scheme;
> proxy_set_header X-Forwarded-Proto https;
> proxy_set_header X-Forwarded-For $remote_addr;
> proxy_set_header X-Forwarded-Port $server_port;
> proxy_set_header X-Forwarded-Server $host;
> proxy_set_header Referrer "no-referrer";
> proxy_connect_timeout 3600;
> proxy_send_timeout 3600;
> proxy_read_timeout 3600;
> proxy_redirect off;
> proxy_buffering off;
> proxy_cache_valid 5m;
> 
> ```

> **Server #1 gateway.conf**
>
> ```
> server {
> 
> listen 80 default_server;
> server_name domain.com www.domain.com forums.domain.com domain.org. domain.net;
> 
> location ^~ /.well-known/acme-challenge {
> proxy_pass http://127.0.0.1:81;
> proxy_set_header Host $host;
> }
> 
> location / {
> return 301 https://$host$request_uri;
> }
> }
> 
> ```

> **Server #1 letsencrypt.conf**
>
> ```
> server {
> listen 127.0.0.1:81 default_server;
> server_name 127.0.0.1; 
> include /etc/nginx/conf.d/proxy.conf;
> location ^~ /.well-known/acme-challenge {
> default_type text/plain;
> root /var/www/letsencrypt;
> }
> }
> 
> ```

> **Server #1 domain.com.conf**
>
> ```
> upstream discourse {	
> server server2.domain.com:443 fail_timeout=5; # This could also be an IP address; in this case, my proxy forwards to a dynamic IP address, so I set the upstream server to a domain name instead of an IP.
> }
> 
> server {
> listen 80; listen [::]:80;
> server_name domain.com www.domain.com forums.domain.com;
> return 301 https://www.domain.com$request_uri;
> }
> 
> server {
> listen 443 ssl http2; listen [::]:443 ssl http2;
> server_name www.domain.com domain.com;
> 
> ssl on;
> ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
> ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
> include /etc/nginx/conf.d/ssl.conf;
> include /etc/nginx/conf.d/header.conf;
> 
> http2_idle_timeout 5m; # up from 3m default
> 
> location / {
> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
> proxy_set_header Host $http_host;
> proxy_redirect off;
> # pass to the upstream discourse server mentioned above
> proxy_pass https://discourse;
> }
> 
> }
> 
> ```

> **Server #1 ssl.conf**
>
> ```
> ssl_dhparam /etc/ssl/certs/dhparam.pem;
> ssl_session_timeout 1d;
> ssl_session_cache shared:SSL:50m;
> ssl_session_tickets off;
> ssl_protocols TLSv1.2 TLSv1.3;
> 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:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK:!AES128';
> ssl_prefer_server_ciphers on;
> ssl_ecdh_curve secp384r1;
> ssl_stapling on;
> ssl_stapling_verify on;
> 
> ```

> **Server #1 header.conf**
>
> ```
> add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
> add_header X-Robots-Tag none;
> add_header X-Download-Options noopen;
> add_header X-Permitted-Cross-Domain-Policies none;
> add_header X-Content-Type-Options "nosniff" always;
> add_header X-XSS-Protection "1; mode=block" always;
> add_header Referrer-Policy "strict-origin" always;
> 
> ```

  

> **Server #2 nginx.conf**
>
> ```
> user www-data www-data;
> worker_processes 4;
> pid /run/nginx.pid;
> 
> # Load dynamic modules. See /usr/share/nginx/README.dynamic.
> # load_module modules/ngx_http_modsecurity_module.so;
> # load_module modules/ngx_http_fancyindex_module.so;
> #include modules/*.so;
> 
> load_module modules/ngx_http_auth_pam_module.so;
> load_module modules/ngx_http_cache_purge_module.so;
> load_module modules/ngx_http_dav_ext_module.so;
> load_module modules/ngx_http_echo_module.so;
> load_module modules/ngx_http_fancyindex_module.so;
> load_module modules/ngx_http_geoip_module.so;
> load_module modules/ngx_http_headers_more_filter_module.so;
> load_module modules/ngx_http_image_filter_module.so;
> #load_module modules/ngx_http_lua_module.so;
> load_module modules/ngx_http_perl_module.so;
> load_module modules/ngx_http_subs_filter_module.so;
> load_module modules/ngx_http_uploadprogress_module.so;
> load_module modules/ngx_http_upstream_fair_module.so;
> load_module modules/ngx_http_xslt_filter_module.so;
> load_module modules/ngx_mail_module.so;
> load_module modules/ngx_nchan_module.so;
> load_module modules/ngx_stream_module.so;
> 
> events {
> worker_connections 1024;
> }
> 
> http {
> 
> ##
> # Basic Settings
> ##
> 
> sendfile on;
> tcp_nopush on;
> tcp_nodelay on;
> keepalive_timeout 180;
> types_hash_max_size 2048;
> include /etc/nginx/mime.types;
> default_type application/octet-stream;
> client_max_body_size 32M;
> autoindex off;
> 
> ##
> # Logging Settings
> ##
> log_format main '$remote_addr - $remote_user [$time_local] "$request" '
> '$status $body_bytes_sent "$http_referer" '
> '"$http_user_agent" "$http_x_forwarded_for"';
> 
> access_log /var/log/nginx/access_log main;
> error_log /var/log/nginx/error_log;
> 
> ##
> # SSL Settings
> ##
> 
> ssl_protocols TLSv1.2 TLSv1.3;
> 
> ssl_session_cache shared:SSL:1m;
> ssl_session_timeout 20m;
> ssl_prefer_server_ciphers on;
> 
> 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';
> 
> map $scheme $fastcgi_https {
> default off;
> https on;
> }
> 
> proxy_ssl_session_reuse on;
> 
> gzip on;
> gzip_disable "msie6";
> gzip_min_length 256;
> gzip_vary on;
> gzip_proxied any;
> gzip_comp_level 5;
> gzip_types
> application/atom+xml
> application/javascript
> application/json
> application/ld+json
> application/manifest+json
> application/rss+xml
> application/vnd.geo+json
> application/vnd.ms-fontobject
> application/x-font-ttf
> application/x-web-app-manifest+json
> application/xhtml+xml
> application/xml
> font/opentype
> image/bmp
> image/svg+xml
> image/x-icon
> text/cache-manifest
> text/css
> text/plain
> text/vcard
> text/vnd.rim.location.xloc
> text/vtt
> text/x-component
> text/x-cross-domain-policy;
> 
> proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=map:8m max_size=1g inactive=24h;
> 
> upstream php-handler {
> server unix:/run/php/php7.2-fpm.sock;
> }
> 
> upstream domain {
> server unix:/run/php/domain.sock;
> }
> 
> upstream domain2 {
> server unix:/run/php/domain2.sock;
> }
> 
> include /etc/nginx/conf.d/php_optimization.conf;
> #include /etc/nginx/conf.d/php-fpm.conf;
> #include /etc/nginx/conf.d/browser-caching.conf;
> include /etc/nginx/conf.d/proxy.conf;
> #include /etc/nginx/conf.d/gdpr.conf;
> #include /etc/nginx/conf.d/gateway.conf;
> #include /etc/nginx/conf.d/letsencrypt.conf;
> #include /etc/nginx/conf.d/site-*.conf;
> #include /etc/nginx/conf.d/restrictions*.conf;
> include /etc/nginx/sites-enabled/*;
> 
> server_names_hash_bucket_size 64;
> 
> # Proxy Server IPs # this should be the IP of your reverse proxy server, or server 1
> set_real_ip_from ip.ad.dr.ess;
> 
> # Cloudflare IPs
> set_real_ip_from 204.93.240.0/24;
> set_real_ip_from 204.93.177.0/24;
> set_real_ip_from 199.27.128.0/21;
> set_real_ip_from 173.245.48.0/20;
> set_real_ip_from 103.21.244.0/22;
> set_real_ip_from 103.22.200.0/22;
> set_real_ip_from 103.31.4.0/22;
> set_real_ip_from 141.101.64.0/18;
> set_real_ip_from 108.162.192.0/18;
> set_real_ip_from 190.93.240.0/20;
> set_real_ip_from 188.114.96.0/20;
> set_real_ip_from 197.234.240.0/22;
> set_real_ip_from 198.41.128.0/17;
> set_real_ip_from 162.158.0.0/15;
> real_ip_header CF-Connecting-IP;
> 
> }
> 
> ```

> **Server #2 proxy.conf**
>
> ```
> proxy_set_header Host $host;
> proxy_set_header X-Real-IP $remote_addr;
> proxy_set_header X-Forwarded-Host $host;
> proxy_set_header X-Forwarded-Protocol $scheme;
> proxy_set_header X-Forwarded-For $remote_addr;
> proxy_set_header X-Forwarded-Port $server_port;
> proxy_set_header X-Forwarded-Server $host;
> proxy_set_header Referer "no-referrer";
> proxy_connect_timeout 3600;
> proxy_send_timeout 3600;
> proxy_read_timeout 3600;
> proxy_redirect off;
> 
> ```

> **Server #2 gateway.conf**
>
> ```
> server {
> 
> listen 80 default_server;
> server_name server1.domain.com domain.com www.domain.com forums.domain.com domain.org domain.net;
> 
> location ^~ /.well-known/acme-challenge {
> proxy_pass http://127.0.0.1:81;
> proxy_set_header Host $host;
> }
> 
> location / {
> return 301 https://$host$request_uri;
> }
> }
> 
> ```

> **Server #2 letsencrypt.conf**
>
> ```
> server {
> listen 127.0.0.1:81 default_server;
> server_name 127.0.0.1; 
> include /etc/nginx/conf.d/proxy.conf;
> location ^~ /.well-known/acme-challenge {
> default_type text/plain;
> root /var/www/letsencrypt;
> }
> }
> 
> ```

> **Server #2 domain.com.conf**
>
> ```
> server {
> listen 80; listen [::]:80;
> server_name domain.com www.domain.com forums.domain.com;
> 
> return 301 https://www.domain.com$request_uri;
> }
> 
> server {
> listen 443 ssl http2; listen [::]:443 ssl http2;
> server_name domain.com;
> ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
> ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.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;
> ssl_prefer_server_ciphers on;
> 
> return 301 https://www.domain.com$request_uri;
> }
> 
> server {
> listen 443 ssl http2; listen [::]:443 ssl http2;
> server_name www.domain.com;
> 
> ssl on;
> ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
> ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.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;
> ssl_prefer_server_ciphers on;
> 
> http2_idle_timeout 5m; # up from 3m default
> 
> 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_read_timeout 180;
> }
> }
> 
> ```

> **Server #2 app.yml**
>
> ```
> ## 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/cron.template.yml"
> - "templates/postgres.template.yml"
> - "templates/redis.template.yml"
> - "templates/sshd.template.yml"
> - "templates/web.template.yml"
> # - "templates/web.ssl.template.yml"
> # - "templates/web.letsencrypt.ssl.template.yml"
> - "templates/web.ratelimited.template.yml"
> - "templates/web.socketed.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"
> #- "templates/cloudflare.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:
> - "2222:22" # ssh
> 
> params:
> db_default_text_search_config: "pg_catalog.english"
> upload_size: 30m
> 
> ## 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
> #version: stable
> 
> 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: 3
> 
> ## TODO: The domain name this Discourse instance will respond to
> DISCOURSE_HOSTNAME: 'www.domain.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: false
> 
> ## 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: 'yourpersonalemail@domain.com'
> 
> ## TODO: The SMTP mail server used to validate new accounts and send notifications
> DISCOURSE_SMTP_ADDRESS: mailserver.domain.com
> DISCOURSE_SMTP_PORT: 587
> DISCOURSE_SMTP_USER_NAME: myserveremail@domain.com
> DISCOURSE_SMTP_PASSWORD: yourpasswordhere
> DISCOURSE_SMTP_ENABLE_START_TLS: true
>   
> ## If you added the Lets Encrypt template, uncomment below to get a free SSL certificate
> LETSENCRYPT_ACCOUNT_EMAIL: yourletsencryptemail@domain.com
> 
> ## The CDN address for this Discourse instance (configured to pull)
> ## see https://meta.discourse.org/t/14857 for details
> #DISCOURSE_CDN_URL: //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:
> - mkdir -p plugins
> - git clone https://github.com/discourse/docker_manager.git
> # Your plugins here
> 
> ## Any custom commands to run after building
> run:
> 
> ```

---

<div class="post-metadata">

### Author: ![vitorl](https://avatars.discourse-cdn.com/v4/letter/v/57b2e6/32.png) [@vitorl](https://meta.discourse.org/u/vitorl)
#### Post date: [November 15, 2018, 9:12am UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/6 "2018-11-15T09:12:13Z")

</div>

I use jwilder/nginx-proxy with discourse just fine.

What I did was to add the following stuff to my app.yml file:

```plaintext
VIRTUAL_HOST: domain.com
VIRTUAL_PORT: 80
LETSENCRYPT_HOST: domain.com
LETSENCRYPT_EMAIL: some@email.com

```

My ports are like this, but I don’t remember anymore why… I think it avoid discourse complaining about host 80 port is being used :

```plaintext
expose:
  - "127.0.0.1:3040:80"`

```

Make sure your container is connected to the nginx network with:

```plaintext
docker_args:
  - "--network=name-of-your-network"

```

To fix discourse not getting the real IP from the host nginx, add this to your `app.yml`:

```
run:
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 172.18.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        types {

```

Make sure the network there matches the subnet of your nginx docker network. You can check with `docker inspect nginx-proxy` and the ip in the command above will always be your gateway but replace last digit for 0.

So for me my nginx-proxy gateway is `172.18.0.1` so that’s why I have it as `172.18.0.0`

---

<div class="post-metadata">

### Author: ![confused](https://avatars.discourse-cdn.com/v4/letter/c/f08c70/32.png) [@confused](https://meta.discourse.org/u/confused)
#### Post date: [November 15, 2018, 1:35pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/7 "2018-11-15T13:35:27Z")

</div>

Thanks, @vitorl!

> [@vitorl](#):
>
> docker\_args: - “–network=name-of-your-network”

This is what I was missing. Thanks.

Real IP snippet is really helpful too.

---

<div class="post-metadata">

### Author: ![brandex07](https://avatars.discourse-cdn.com/v4/letter/b/7ab992/32.png) [@brandex07](https://meta.discourse.org/u/brandex07)
#### Post date: [January 20, 2019, 4:24pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/8 "2019-01-20T16:24:45Z")

</div>

@vitorl @confused

Could one of you show me your docker-compose file? I am having trouble getting this working. Here is mine:

```
## 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:
  - "172.17.0.1:8080:80" # http
  - "172.17.0.1:4434:443" # https

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: "768MB"

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

  ## TODO: The domain name this Discourse instance will respond to
  ## Required. Discourse will not work with a bare IP number.
  DISCOURSE_HOSTNAME: discourse.bakeitcookbook.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: 'bkalashian@gmail.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.mailgun.org
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: ...
  DISCOURSE_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: me@example.com

  ## The CDN address for this Discourse instance (configured to pull)
  ## see https://meta.discourse.org/t/14857 for details
  #DISCOURSE_CDN_URL: //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

environment:
  - "VIRTUAL_PORT:80"
  - "VIRTUAL_HOST:discourse.bakeitcookbook.com"
  - "LETSENCRYPT_HOST:discourse.bakeitcookbook.com"
  - "LETSENCRYPT_EMAIL:bkalashian@gmail.com"

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

docker_args:
  - "--network=nginx-proxy"

## 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"i
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 172.17.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        types {

```

What am I doing wrong?

---

<div class="post-metadata">

### Author: ![vitorl](https://avatars.discourse-cdn.com/v4/letter/v/57b2e6/32.png) [@vitorl](https://meta.discourse.org/u/vitorl)
#### Post date: [January 25, 2019, 1:56am UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/9 "2019-01-25T01:56:42Z")

</div>

Here is mine, beware that I have a lot of custom settings, so it may not work out of the box for you.

This is my app.yml

```plaintext
## After making changes to this file, you MUST rebuild
## /var/discourse/launcher rebuild app

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"

expose:
  - "127.0.0.1:3030:80"  

docker_args:
  - "--network=name-of-your-docker-network"
  - "--sysctl net.core.somaxconn=4096"

params:
  db_default_text_search_config: "pg_catalog.english"
  db_work_mem: "40MB"
 
env:
  LANG: en_US.UTF-8

  VIRTUAL_HOST: redacted.app,www.redacted.app,redacted.com.br,www.redacted.com.br
  VIRTUAL_PORT: 80
  LETSENCRYPT_HOST: redacted.app,www.redacted.app,redacted.com.br,www.redacted.com.br
  LETSENCRYPT_EMAIL: email@email.com

  DISCOURSE_HOSTNAME: redacted.app
  DISCOURSE_DEVELOPER_EMAILS: 'email@email.com'

  DISCOURSE_SMTP_ADDRESS: smtp.sendgrid.net
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: apikey
  DISCOURSE_SMTP_PASSWORD: "password"
  DISCOURSE_SMTP_ENABLE_START_TLS: true
  SSL_POLICY: Mozilla-Modern         

volumes:
  - volume:
      host: /opt/discourse/data
      guest: /shared
  - volume:
      host: /opt/discourse/data/log
      guest: /var/log

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-sitemap.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/angusmcleod/discourse-locations.git
          - git clone https://github.com/angusmcleod/discourse-events.git

  after_web_config:
    - replace:
        filename: /etc/nginx/nginx.conf
        from: /sendfile.+on;/
        to: |
          server_names_hash_bucket_size 64;
          sendfile on;
    - file:
        path: /etc/nginx/conf.d/discourse_redirect_1.conf
        contents: |
          server {
            server_name www.redacted.app www.redacted.com.br redacted.com.br;
            return 301 https://redacted.app$request_uri;
          }

run:
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 172.18.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        types {

```

Do not copy and paste. You have to change some stuff, this also assumes you are using sendgrid. You need to replace the domains, email, sendgrid apikey password.

Good luck.

---

<div class="post-metadata">

### Author: ![vitorl](https://avatars.discourse-cdn.com/v4/letter/v/57b2e6/32.png) [@vitorl](https://meta.discourse.org/u/vitorl)
#### Post date: [April 5, 2019, 1:40pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/10 "2019-04-05T13:40:14Z")

</div>

For some reason my real ip snippet is not working anymore. Not sure if it’s because of cloudflare CDN. do you have cloudflare and is it working with you?

---

<div class="post-metadata">

### Author: ![Stephen](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stephen/32/95011_2.png) [@Stephen](https://meta.discourse.org/u/Stephen)
#### Post date: [April 5, 2019, 3:12pm UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/11 "2019-04-05T15:12:59Z")

</div>

If the orange cloud is on then try turning it off does that solve the problem?

You can’t use Cloudflare caching or optimisations on discourse code anyhow, so it’s of limited value.

---

<div class="post-metadata">

### Author: ![vitorl](https://avatars.discourse-cdn.com/v4/letter/v/57b2e6/32.png) [@vitorl](https://meta.discourse.org/u/vitorl)
#### Post date: [April 6, 2019, 12:19am UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/12 "2019-04-06T00:19:59Z")

</div>

I use Cloudflare to hide my server IP and won’t disable it, even if it means not getting the real IP’s.

What I have noticed is that if I use the Cloudflare template I see my docker IP in the access.log, regardless of having this snippet or not:

```plaintext
  - replace:
      filename: /etc/nginx/conf.d/discourse.conf
      from: "types {"
      to: |
        set_real_ip_from 172.18.0.0/24;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
        types {

```

If I remove the Cloudflare template and use just the snippet above I see Cloudflare IP’s on my logs.

---

<div class="post-metadata">

### Author: ![JammyDodger](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/jammydodger/32/254611_2.png) [@JammyDodger](https://meta.discourse.org/u/JammyDodger)
#### Post date: [June 19, 2022, 7:49am UTC](https://meta.discourse.org/t/discourse-with-jwilder-nginx-proxy-howtos/94604/14 "2022-06-19T07:49:10Z")

</div>


