Multisite configuration with Docker

You may wish to host multiple domains on a singled Docker setup. To do so follow these instructions:

We strongly recommend you run separate web and data containers

The standalone container is extremely easy to configure, however has a couple of drawbacks

  1. You will need to stop your site when bootstrapping a new image
  2. You run through the db bootstrap only when needed

Multisite is a fairly advanced topic, learn about hooks before attempting this.

Understand hooks

Discourse templates use pups, its rules are simple and powerful.

Each rule you run may define a hook:

    cd: some/path
    hook: my_hook
      - echo 1

Later on in your container you can insert rules before or after a hook:

    - exec: echo "I ran before"
     - exec: echo "I ran after"

So in the example above you will see output like the following:

I ran before
I ran after

You can read through the templates in /var/discourse/templates to see what hooks you have available.

Amend your standalone container to provision the site and talk to it

Replace the entire hooks section with:

     - exec: sudo -u postgres createdb b_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database b_discourse to discourse;
          cmd: sudo -u postgres psql b_discourse
          raise_on_fail: false

     - exec: /bin/bash -c 'sudo -u postgres psql b_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql b_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql b_discourse <<< "create extension if not exists pg_trgm;"'

    - exec:
        cd: $home/plugins
          - mkdir -p plugins
          - git clone
    - file:
        path: $home/config/multisite.yml
        contents: |
           adapter: postgresql
           database: b_discourse
           pool: 25
           timeout: 5000
           db_id: 2

    - exec: cd /var/www/discourse && sudo -E -u discourse bundle exec rake multisite:migrate

There are 3 hooks in play:

  1. after_postgres, ensures that after postgres is installed an additional db called b_discourse is created with the appropriate permissions.

  2. before_bundle_exec, ensures docker_manager is in place and that the multisite.yml file is in place (which defines where to find the databases)

  3. after_bundle_exec, runs the custom db migration task rake multisite:migrate this ensures all the dbs are up to date.

The above sample can be split into data container / app container if needed (just run the after_postgres hook in the data container and the rest in web container)

The above sample can be extended to provision even more DBs, to do so, provision more dbs by duplicating the create db etc calls, and make sure you have additional sites in multisite.yml

Make sure you amend the host_names node in multisite.yml to match the actual host name you wish to host.

Multisite configuration step by step
How to create 1000 subcategories in a scalable way?
How does multisite.yml look?
Multiple Forums in a Single Installation
Pros and cons of multiple standalone containers vs one for multiple Discourse installs?
Multiple Discourses, multiple containers, one server
Will I need to start over with multisite configuration from standalone version?
Success - New Multisite Install on Dedicated server using ServerPilot, Nginx and Apache
Installation wizard not appearing in multisite installs
Multisite Installation
Multiple Discourse Installs - Single Server
Starting a second Discourse forum on the same VPS
Multi-site Installation
How do you set up multiple discourse forums on the same server?
Need proper documentation for multisite discourse with docker
Discourse multisite installation help with digitalocean
How to move from standalone container to separate web and data containers
Sandbox and test discourse on host?
Second discourse instance on the same server
Install Discourse on Plesk / Ubuntu 14 without Docker
Pros and cons of a multisite installation
Multiple discourse instances in a single server
Disabling/restricting user search
Multisite viability - 2 read-only and 1 active
Multisite installation with seperated smtp emails
Can I log into multiple instances of discourse simultaneously?
Need help with multisite configuration
Multisite for small collection of sites for a family?
Discourse for 3 different Countrys
Multiple discourse instances in a single server
How to move from standalone container to separate web and data containers
How to move from standalone container to separate web and data containers
Three sites in one setup
Totally Walled-Off Groups
Using a launcher built docker image in docker-compose
Are there other workarounds to not having category moderation such as multiple instances of the board?
Adding a forum to an existic DigitalOcean droplet
App.yml shared volumes for a two website setup
Two standalone instances on one server?
How can Admin login while Read-only mode is enabled on multisite?
Stable branch assets won't compile
Hosting Plan Suggestion for 500 Concurrent Online Users
Stable branch assets won't compile
Pull request for Wikis?
PAID Require Discourse expert for extreme customization
Multitenat Dockerize Discourse
Secure cookie configuration
Add path to cookie
Contabo experience with Plesk, Webmin or other server-admin setup?
I want to host hundreds of instances of Discourse
Best approach for test and production instance of discourse
Integration with .NET MVC application for a SaaS platform
Feature proposal: Customizations based on primary group membership, a.k.a. tenancy 'lite'
How do I set category tracking level defaults historically
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column categories.search_priority does not exist
Can we change lets encrypt script?

What is the correct way to initialize a new database in a multisite configuration?

This is what I’m doing so far:

if [ $# -eq 0 ]
    echo "No arguments supplied"
    exit 1

echo "Creating database..."
sudo -u postgres createdb $1 || exit 0
echo "Granting privileges..."
sudo -u postgres psql $1 <<< "alter schema public owner to discourse;"
sudo -u postgres psql $1 <<< "grant all privileges on database \"$1\" to discourse;"
echo "Creating extensions..."
sudo -u postgres psql $1 <<< "create extension if not exists hstore;"
sudo -u postgres psql $1 <<< "create extension if not exists pg_trgm;"
echo "Running db:migrate..."
RAILS_DB=$1 cd /var/www/discourse && sudo -E -u discourse bundle exec rake db:migrate
echo "Done"

This typically results in a broken site because seeding failed. However, simply re-running the db:migrate step typically fixes it.


Sorry to bump this, but… any chance to get some advice on this by a team member? :cry:

I would strongly recommend you run in a multicontainer environment if you are doing multisite, then use createdb to create the databases by entering the postgres container.

1 Like

But createdb isn’t enough, the DB also needs to be seeded / migrated, right?

seeding and migrating is easy cause you already have a step for that. That is what multisite:migrate does.

I think I had already tried running multisite:migrate from within the standalone container, and it didn’t work out – but I’m not sure about that anymore.

I’d really like to avoid having to run a multi-container setup as it doesn’t really have an advantage for me, and brings a lot of additional complexity (including having to migrate from my current setup).

One significant advantage, and why I think that it makes sense even for a single site, is that you can bootstrap from the command line without taking the site down for 5-15 minutes.

I think what’s wrong with your db:migrate is that it’s running for the main site, not the one you just created. Add RAILS_DB=secondsite before your rake. But the multisite:migrate should do it, I’d think (but I’m far from an expert).


Bootstrapping has become such a rare event (thanks to Docker Manager) that this simply isn’t a strong enough argument to convince me to spend some hours adding complexity :slight_smile:

Good catch – the variable I’m setting might not actually come through. I’ve moved it just before the bundle command and will report back the next time I create a multisite tenant. Thanks!


Some time ago I had time to configure multisite with 3 containers: data, web, and redis. All was working fine until one day I upgraded, and bam, something wrong happened and I had to revert to single site from backups.

Now I have a little time and wish to get this multisite setup running again. But this time, I want to clarify some things before I break stuff again :slight_smile:

  • the admin dashboard used to have a blue “upgrade” button linking to /admin/upgrade, somehow this one disappeared for me. Do you know how to make it appear again? Maybe that simple thing already taints my installation for split containers.
  • is the “button upgrade” still available in multisite mode then?
  • how would you setup to have a plugin that appears on one instance, but not the next? This would be useful for staging a plugin in production environment, or to add a site-specific plugin (e.g., a theme).

All sites on a multisite install share the same plugins. If the plugin is broken all sites are affected.



I want to setup discourse configuration with 2 site (2 domain).

My configuration: [YAML] # this is the all-in-one, standalone Discourse Docker container template ## ## - I only get 1 site working - 2nd get redirection to 1st one (and ssl certificate problems), is there any bad in my config?

I thought that can be ssl problem and with commenting:

#  - "templates/web.ssl.template.yml"
#  - "templates/web.letsencrypt.ssl.template.yml"
#  - "443:443" # https

Discourse did not work with this changes (after rebuild). Even if I start fresh installation with this changes, it did not work on http (port 80).

So is there any bad multisite configuration with my file?

1 Like

If you only need two sites then you are probably better off setting up two separate Discourse instances.

1 Like

It will be 3 or more in the future I think.
In two seperate instances you mean two containers with nginx reverse proxy/haproxy?

Ok, there is Cache problem with redirection. HTTP work but on second site I get error (500 on site):

[secondsite] Started GET "/" for **** at 2017-11-22 21:15:11 +0000
[secondsite] Processing by ListController#latest as HTML
[secondsite] Completed 500 Internal Server Error in 37ms (ActiveRecord: 11.8ms)
[secondsite] ActiveRecord::StatementInvalid (PG::UndefinedTable: 
ERROR:  relation "groups" does not exist
LINE 1: SELECT "groups"."id", "groups"."name" FROM "groups" 
: SELECT "groups"."id", "groups"."name" FROM "groups" ORDER BY 
 "groups"."name" ASC)
 /var/www/discourse/vendor/bundle/ruby/2.4.0/gems/rack-mini-profiler-0.10.5/lib/patches/db/pg.rb:90:in `async_exec'
 [secondsite] Started GET "/" for **** at 2017-11-22 21:15:12 +0000
 [secondsite] Processing by ListController#latest as HTML
 [secondsite] Completed 500 Internal Server Error in 4ms (ActiveRecord: 0.7ms)
 [secondsite] ActiveRecord::StatementInvalid (PG::UndefinedTable: 
 ERROR:  relation "groups" does not exist 
 LINE 1: SELECT "groups"."id", "groups"."name" FROM "groups" ORDER BY...
 : SELECT "groups"."id", "groups"."name" FROM "groups" ORDER BY "groups"."name" ASC)
/var/www/discourse/vendor/bundle/ruby/2.4.0/gems/rack-mini-profiler-0.10.5/lib/patches/db/pg.rb:90:in `async_exec'

I suspect that the second database didn’t get created.


I found base in psql:

b_discourse-# \dS
 pg_catalog | pg_prepared_xacts               | view  | postgres
 pg_catalog | pg_proc                         | table | postgres
 pg_catalog | pg_range                        | table | postgres
 pg_catalog | pg_replication_origin           | table | postgres
 pg_catalog | pg_replication_origin_status    | view  | postgres
 pg_catalog | pg_replication_slots            | view  | postgres
 pg_catalog | pg_rewrite                      | table | postgres
 pg_catalog | pg_roles                        | view  | postgres
 pg_catalog | pg_rules                        | view  | postgres
 pg_catalog | pg_seclabel                     | table | postgres
 pg_catalog | pg_seclabels                    | view  | postgres
 pg_catalog | pg_settings                     | view  | postgres
 pg_catalog | pg_shadow                       | view  | postgres
 pg_catalog | pg_shdepend                     | table | postgres
 pg_catalog | pg_shdescription                | table | postgres
 pg_catalog | pg_shseclabel                   | table | postgres
 pg_catalog | pg_stat_activity                | view  | postgres
 pg_catalog | pg_stat_all_indexes             | view  | postgres
 pg_catalog | pg_stat_all_tables              | view  | postgres
 pg_catalog | pg_stat_archiver                | view  | postgres
 pg_catalog | pg_stat_bgwriter                | view  | postgres
 pg_catalog | pg_stat_database                | view  | postgres
 pg_catalog | pg_stat_database_conflicts      | view  | postgres
 pg_catalog | pg_stat_replication             | view  | postgres
 pg_catalog | pg_stat_ssl                     | view  | postgres
 pg_catalog | pg_stat_sys_indexes             | view  | postgres
 pg_catalog | pg_stat_sys_tables              | view  | postgres
 pg_catalog | pg_stat_user_functions          | view  | postgres
 pg_catalog | pg_stat_user_indexes            | view  | postgres
 pg_catalog | pg_stat_user_tables             | view  | postgres
 pg_catalog | pg_stat_xact_all_tables         | view  | postgres
 pg_catalog | pg_stat_xact_sys_tables         | view  | postgres
 pg_catalog | pg_stat_xact_user_functions     | view  | postgres
 pg_catalog | pg_stat_xact_user_tables        | view  | postgres
 pg_catalog | pg_statio_all_indexes           | view  | postgres
 pg_catalog | pg_statio_all_sequences         | view  | postgres
 pg_catalog | pg_statio_all_tables            | view  | postgres
 pg_catalog | pg_statio_sys_indexes           | view  | postgres
 pg_catalog | pg_statio_sys_sequences         | view  | postgres
 pg_catalog | pg_statio_sys_tables            | view  | postgres
 pg_catalog | pg_statio_user_indexes          | view  | postgres
 pg_catalog | pg_statio_user_sequences        | view  | postgres
 pg_catalog | pg_statio_user_tables           | view  | postgres
 pg_catalog | pg_statistic                    | table | postgres
 pg_catalog | pg_stats                        | view  | postgres
 pg_catalog | pg_tables                       | view  | postgres
 pg_catalog | pg_tablespace                   | table | postgres
 pg_catalog | pg_timezone_abbrevs             | view  | postgres
 pg_catalog | pg_timezone_names               | view  | postgres
 pg_catalog | pg_transform                    | table | postgres
 pg_catalog | pg_trigger                      | table | postgres
 pg_catalog | pg_ts_config                    | table | postgres
 pg_catalog | pg_ts_config_map                | table | postgres
 pg_catalog | pg_ts_dict                      | table | postgres
 pg_catalog | pg_ts_parser                    | table | postgres
 pg_catalog | pg_ts_template                  | table | postgres
 pg_catalog | pg_type                         | table | postgres
 pg_catalog | pg_user                         | view  | postgres
 pg_catalog | pg_user_mapping                 | table | postgres
 pg_catalog | pg_user_mappings                | view  | postgres
 pg_catalog | pg_views                        | view  | postgres
(106 rows)

Any ideas?

Not really. Perhaps it didn’t get seeded and migrated.

I thought that this line is for this:

 - exec: cd /var/www/discourse && sudo -E -u discourse bundle exec rake multisite:migrate

I used this command in app and secondsite works! Now only SSL to configure :slight_smile:

1 Like

How exactly did you fix the issue? I keep getting

I, [2017-12-22T09:20:11.912278 #15] INFO -- : &gt; cd /var/www/discourse &amp;&amp; su discourse -c 'bundle exec rake db:migrate'

rake aborted!

PG::ConnectionBad: could not connect to server: No such file or directory

 Is the server running locally and accepting

 connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

Same thing locally and on the remote server. Manually can access/enter data container and Postgres with no probs.