Despite at least one reference which purports to describe the process I found it very difficult to work out how to run multiple Discourse instances on the same hosting infrastructure (using an instance of nginx running on the host to proxy the Discourse instances).
Apparently there’s a way to run Discourse in a “multisite” mode, with multiple instances from a single code installation, but I have not yet seen a real description of how that works.
I’ve gone the route of two independent code installations, but a common data container (two separate databases on the same server). After a lot of fiddling, I’ve now got two Discourse instances running community.oeru.org and forums.oeru.org, with two separate Docker web containers and a single data container on a single server.
For the benefit of others wanting to achieve this, here’s how I did it:
1. Set up a new Postgres database in the data container - after trying to create the second database via my data.yml file (in containers dir), which didn’t seem to work - I logged directly into the container to create the second database (the launcher command is run from within the Discourse root directory, in my case /home/www/discourse):
./launcher enter data
and I ran the following (my second database is “discourse2”, and is owned by the same discourse user (and password) as my other Discourse instance, with db of “discourse”):
sudo -u postgres createdb discourse2
sudo -u postgres psql discourse2 <<END
GRANT ALL PRIVILEGES ON DATABASE discourse2 TO discourse;
ALTER SCHEMA public OWNER TO discourse;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
END
and then exited:
CTRL-D
2. I restarted the data container (this seemed to be necessary, although I’m not sure why):
./launcher restart data
3. I created a configuration for a second web container (my first is just web.yml, so I called the second web2.yml) in which I put:
# IMPORTANT: SET A SECRET PASSWORD in Postgres for the Discourse User
# TODO: change SOME_SECRET in this template
templates:
# - "templates/redis.template.yml"
- "templates/sshd.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
expose:
# increment the ports by one from the convention initiated by first discourse
- "8081:80"
- "2223:22"
params:
## Which Git revision should this container use? (default: tests-passed)
#version: tests-passed
env:
LANG: en_US.UTF-8
## TODO: How many concurrent web requests are supported?
## With 2GB we recommend 3-4 workers, with 1GB only 2
UNICORN_WORKERS: 3
## TODO: configure connectivity to the databases
DISCOURSE_DB_SOCKET: ''
DISCOURSE_DB_USERNAME: discourse
DISCOURSE_DB_PASSWORD: '[***discourse user password***]'
DISCOURSE_DB_NAME: discourse2
DISCOURSE_DB_HOST: data
DISCOURSE_REDIS_HOST: data
##
## 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: '[***my email***]'
##
## TODO: The domain name this Discourse instance will respond to
DISCOURSE_HOSTNAME: 'forums.oeru.org'
##
## TODO: The mailserver this Discourse instance will use
DISCOURSE_SMTP_ADDRESS: smtp.mandrillapp.com # (mandatory)
DISCOURSE_SMTP_PORT: 587 # (optional)
DISCOURSE_SMTP_USER_NAME: [***my email***] # (optional)
DISCOURSE_SMTP_PASSWORD: [***my password***] # (optional)
DISCOURSE_SMTP_ENABLE_START_TLS: True
##
## The CDN address for this Discourse instance (configured to pull)
#DISCOURSE_CDN_URL: //discourse-cdn.example.com
volumes:
- volume:
host: /home/www/discourse/shared2/web
guest: /shared
- volume:
host: /home/www/discourse/shared2/web/log/var-log
guest: /var/log
## Use 'links' key to link containers together, aka use Docker --link flag.
links:
- link:
name: data
alias: data
## The docker manager plugin allows you to one-click upgrade Discouse
## http://discourse.example.com/admin/docker
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
- mkdir -p plugins
- git clone https://github.com/discourse/docker_manager.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;}'`
4. Build the new web container
./launcher bootstrap web2
and after it finishes
./launcher start web2
At this point, you should be able to check http://localhost:8081 on the host using a text browser like links or lynx to confirm that it’s actually producing something… Finally, setting up the nginx proxy.
5. Then I created the following file in /etc/nginx/sites-available (you can easily upgrade this configuration to use SSL and create another server definition to redirect traffic to port 80 to port 443 - now that we’ve been accepted to the Let’s Encrypt beta, I’ll be doing that shortly and will post an update afterwards):
upstream discourse2 {
#fail_timeout is optional; I throw it in to see errors quickly
server 127.0.0.1:8081 fail_timeout=5;
}
# configure the virtual host
server {
listen 5.9.142.102:80;
# replace with your domain name
server_name forums.oeru.org;
root /var/www; # placeholder only!
index index.html;
#Rewrite
# assets
location ~* \.(jpg|jpeg|gif|png|bmp|ico|pdf|txt|css|js) {
try_files $uri @discourse2;
add_header Cache-Control public;
add_header Cache-Control must-revalidate;
expires 7d;
}
# example.com/a.html gets redirected to example.com/a
location ~* \.html$ {
rewrite ^(.*)\/index.html$ $1 permanent;
rewrite ^(.+)\.html$ $scheme://$host$1 permanent;
}
# example.com/foo/ loads example.com/foo/index.html
location ~* ^(.*)/$ {
try_files $1.html $1/index.html @discourse2;
}
# anything else not processed by the above rules:
# * example.com/a will load example.com/a.html
# * or if that fails, example.com/a/index.html
location / {
rewrite ^(.*)\/index$ $1 permanent;
try_files $uri.html $uri/index.html @discourse2;
}
location @discourse2 {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://discourse2;
}
}
And there you have it - our second Discourse instance, “forums”, is now available on the same host as our “community” Discourse instance… Hope this helps someone. Any feedback on this approach would be welcome!