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 as a software-level firewall.
- The main domain is listed as domain.com and Discourse runs on 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: