How to Set Up Discourse Multisite with Letsencrypt SSL

Through trials & errors, we finally managed to set up Discourse multisite with Letsencrypt SSL. Below is the step-by-step guide on how to implement it.

Note: Official Discourse does not support multiple SMTP for multisite, hence it is not possible to use different SMTP services for multisite installs.

  1. During bootstrap, instead of following the first time set up prompt, press CTRL + C to exit and manually edit the app.yml using command: nano containers/app.yml

  2. Example content of manually edited 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/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:

    • “80” # http

    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: “3584MB”

    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

    DISCOURSE_HOSTNAME: 'test1.domain.com
    VIRTUAL_HOST: 'test1.domain.com,test2.domain.com,test3.domain.com,test4.domain.com,test5.domain.com
    LETSENCRYPT_HOST: 'test1.domain.com,test2.domain.com,test3.domain.com,test4.domain.com,test5.domain.com
    LETSENCRYPT_EMAIL: ‘name@email.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: ‘name@email.com’

    TODO: The SMTP mail server used to validate new accounts and send notifications

    DISCOURSE_SMTP_ADDRESS: smtp.sparkpostmail.com # required
    DISCOURSE_SMTP_PORT: 587 # (optional, default 587)
    DISCOURSE_SMTP_USER_NAME: SMTP_Injection # required
    DISCOURSE_SMTP_PASSWORD: 999api999password999here999 # required, WARNING the char ‘#’ in pw can cause problems!
    #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: name@email.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_postgres:
    - 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: sudo -u postgres createdb c_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database c_discourse to discourse;
          cmd: sudo -u postgres psql c_discourse
          raise_on_fail: false
    
     - exec: /bin/bash -c 'sudo -u postgres psql c_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql c_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql c_discourse <<< "create extension if not exists pg_trgm;"'
     - exec: sudo -u postgres createdb d_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database d_discourse to discourse;
          cmd: sudo -u postgres psql d_discourse
          raise_on_fail: false
    
     - exec: /bin/bash -c 'sudo -u postgres psql d_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql d_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql d_discourse <<< "create extension if not exists pg_trgm;"'
     - exec: sudo -u postgres createdb e_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database e_discourse to discourse;
          cmd: sudo -u postgres psql e_discourse
          raise_on_fail: false
    
     - exec: /bin/bash -c 'sudo -u postgres psql e_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql e_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql e_discourse <<< "create extension if not exists pg_trgm;"'
    

    after_code:
    - exec:
    cd: $home/plugins
    cmd:
    - git clone https://github.com/discourse/docker_manager.git
    before_bundle_exec:
    - file:
    path: $home/config/multisite.yml
    contents: |
    secondsite:
    adapter: postgresql
    database: b_discourse
    pool: 25
    timeout: 5000
    db_id: 2
    host_names:
    - test2.domain.com
    thirdsite:
    adapter: postgresql
    database: c_discourse
    pool: 25
    timeout: 5000
    db_id: 3
    host_names:
    - test3.domain.com
    fourthsite:
    adapter: postgresql
    database: d_discourse
    pool: 25
    timeout: 5000
    db_id: 4
    host_names:
    - test4.domain.com
    fifthsite:
    adapter: postgresql
    database: e_discourse
    pool: 25
    timeout: 5000
    db_id: 5
    host_names:
    - test5.domain.com

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

    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=‘noreply@domain.com’”
    • exec: echo “End of custom commands”

The edited parts are:

  1. only expose one port 80 and do not map it.

    expose:

    • “80” # http
  2. Edit the DISCOURSE_HOSTNAME and add VIRTUAL_HOST, LETSENCRYPT_HOST & LETSENCRYPT_EMAIL

    DISCOURSE_HOSTNAME: 'test1.domain.com
    VIRTUAL_HOST: 'test1.domain.com,test2.domain.com,test3.domain.com,test4.domain.com,test5.domain.com
    LETSENCRYPT_HOST: 'test1.domain.com,test2.domain.com,test3.domain.com,test4.domain.com,test5.domain.com
    LETSENCRYPT_EMAIL: ‘name@email.com’

  3. Edit DISCOURSE_DEVELOPER_EMAILS, DISCOURSE_SMTP_ADDRESS, DISCOURSE_SMTP_PORT (applicable for SparkPost), DISCOURSE_SMTP_USER_NAME & DISCOURSE_SMTP_PASSWORD

    DISCOURSE_DEVELOPER_EMAILS: ‘name@email.com’

    TODO: The SMTP mail server used to validate new accounts and send notifications

    DISCOURSE_SMTP_ADDRESS: smtp.sparkpostmail.com # required
    DISCOURSE_SMTP_PORT: 587 # (optional, default 587)
    DISCOURSE_SMTP_USER_NAME: SMTP_Injection # required
    DISCOURSE_SMTP_PASSWORD: 999api999password999here999 # required, WARNING the char ‘#’ in pw can cause problems!

  4. Add this whole chunk of code after hooks: depending on how many sites you want. Example listed below are for 5 sites: b_discourse, c_discourse, d_discourse & e_discourse. If just two sites then b_discourse is sufficient and do not include c_discourse, d_discourse & e_discourse codes.

    hooks:
    after_postgres:
    - 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: sudo -u postgres createdb c_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database c_discourse to discourse;
          cmd: sudo -u postgres psql c_discourse
          raise_on_fail: false
    
     - exec: /bin/bash -c 'sudo -u postgres psql c_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql c_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql c_discourse <<< "create extension if not exists pg_trgm;"'
     - exec: sudo -u postgres createdb d_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database d_discourse to discourse;
          cmd: sudo -u postgres psql d_discourse
          raise_on_fail: false
    
     - exec: /bin/bash -c 'sudo -u postgres psql d_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql d_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql d_discourse <<< "create extension if not exists pg_trgm;"'
     - exec: sudo -u postgres createdb e_discourse || exit 0
     - exec:
          stdin: |
            grant all privileges on database e_discourse to discourse;
          cmd: sudo -u postgres psql e_discourse
          raise_on_fail: false
    
     - exec: /bin/bash -c 'sudo -u postgres psql e_discourse <<< "alter schema public owner to discourse;"'
     - exec: /bin/bash -c 'sudo -u postgres psql e_discourse <<< "create extension if not exists hstore;"'
     - exec: /bin/bash -c 'sudo -u postgres psql e_discourse <<< "create extension if not exists pg_trgm;"'
    
  5. Add this chunk of code after after_code: section.

    before_bundle_exec:
    - file:
    path: $home/config/multisite.yml
    contents: |
    secondsite:
    adapter: postgresql
    database: b_discourse
    pool: 25
    timeout: 5000
    db_id: 2
    host_names:
    - test2.domain.com
    thirdsite:
    adapter: postgresql
    database: c_discourse
    pool: 25
    timeout: 5000
    db_id: 3
    host_names:
    - test3.domain.com
    fourthsite:
    adapter: postgresql
    database: d_discourse
    pool: 25
    timeout: 5000
    db_id: 4
    host_names:
    - test4.domain.com
    fifthsite:
    adapter: postgresql
    database: e_discourse
    pool: 25
    timeout: 5000
    db_id: 5
    host_names:
    - test5.domain.com

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

  6. Edit SiteSetting.notification_email to noreply@domain.com (applicable for using SparkPost)

    • exec: rails r “SiteSetting.notification_email=‘noreply@domain.com’”
  7. Press CTRL + O to save edits followed by CTRL + X to exit. Enter the following command to bootstrap: ./launcher bootstrap app

  8. Applicable for using SparkPost: after bootstrap successfully, you should be able to create admin account using your predefined admin email in app.yml for test1.domain.com. However, you will not be able to create admin account for other sites as they will try to send out emails from noreply@test2.domain.com, noreply@test3.domain.com, noreply@test4.domain.com & noreply@test5.domain.com but will be rejected by SparkPost due to policy_rejection.

    error_code 550
    raw_reason 550 5.7.1 Unconfigured Sending Domain <test2.domain.com>

To resolve this problem, add test2.domain.com, test3.domain.com, test4.domain.com & test5.domain.com to SparkPost Sending Domains and verify using DKIM records set in DNS settings of the (sub)domains. After you have verified the sending domains successfully SparkPost will still take a couple minutes to change the sending domains’ status to Ready to send. Then you will be able to sign up an admin account using the predefined developer email address and receive the ‘Confirm your new account’ verification email from SparkPost.

Hi, tried to follow your instruction but did not succeed.

Error message below:

x86_64 arch detected.
Ensuring launcher is up to date
Fetching origin
Launcher is up-to-date
Stopping old container
+ /usr/bin/docker stop -t 60 favorite-apps
favorite-apps
/usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups.rb
/usr/local/bin/pups --stdin
I, [2022-02-03T14:57:31.860425 #1]  INFO -- : Reading from stdin
I, [2022-02-03T14:57:31.869172 #1]  INFO -- : > locale-gen $LANG && update-locale
I, [2022-02-03T14:57:31.920465 #1]  INFO -- : Generating locales (this might take a while)...
Generation complete.

I, [2022-02-03T14:57:31.920669 #1]  INFO -- : > mkdir -p /shared/postgres_run
I, [2022-02-03T14:57:31.925169 #1]  INFO -- : 
I, [2022-02-03T14:57:31.925560 #1]  INFO -- : > chown postgres:postgres /shared/postgres_run
I, [2022-02-03T14:57:31.931056 #1]  INFO -- : 
I, [2022-02-03T14:57:31.931434 #1]  INFO -- : > chmod 775 /shared/postgres_run
I, [2022-02-03T14:57:31.935199 #1]  INFO -- : 
I, [2022-02-03T14:57:31.935497 #1]  INFO -- : > rm -fr /var/run/postgresql
I, [2022-02-03T14:57:31.942838 #1]  INFO -- : 
I, [2022-02-03T14:57:31.943226 #1]  INFO -- : > ln -s /shared/postgres_run /var/run/postgresql
I, [2022-02-03T14:57:31.947521 #1]  INFO -- : 
I, [2022-02-03T14:57:31.947821 #1]  INFO -- : > socat /dev/null UNIX-CONNECT:/shared/postgres_run/.s.PGSQL.5432 || exit 0 && echo postgres already running stop container ; exit 1
2022/02/03 14:57:31 socat[21] E connect(6, AF=1 "/shared/postgres_run/.s.PGSQL.5432", 36): No such file or directory
I, [2022-02-03T14:57:31.956189 #1]  INFO -- : 
I, [2022-02-03T14:57:31.956407 #1]  INFO -- : > rm -fr /shared/postgres_run/.s*
I, [2022-02-03T14:57:31.960562 #1]  INFO -- : 
I, [2022-02-03T14:57:31.960778 #1]  INFO -- : > rm -fr /shared/postgres_run/*.pid
I, [2022-02-03T14:57:31.970516 #1]  INFO -- : 
I, [2022-02-03T14:57:31.970777 #1]  INFO -- : > mkdir -p /shared/postgres_run/13-main.pg_stat_tmp
I, [2022-02-03T14:57:31.976012 #1]  INFO -- : 
I, [2022-02-03T14:57:31.977572 #1]  INFO -- : > chown postgres:postgres /shared/postgres_run/13-main.pg_stat_tmp
I, [2022-02-03T14:57:31.985585 #1]  INFO -- : 
I, [2022-02-03T14:57:31.997928 #1]  INFO -- : File > /etc/service/postgres/run  chmod: +x  chown: 
I, [2022-02-03T14:57:32.019087 #1]  INFO -- : File > /etc/service/postgres/log/run  chmod: +x  chown: 
I, [2022-02-03T14:57:32.026553 #1]  INFO -- : File > /etc/runit/3.d/99-postgres  chmod: +x  chown: 
I, [2022-02-03T14:57:32.033903 #1]  INFO -- : File > /root/upgrade_postgres  chmod: +x  chown: 
I, [2022-02-03T14:57:32.034578 #1]  INFO -- : > chown -R root /var/lib/postgresql/13/main
I, [2022-02-03T14:57:33.172253 #1]  INFO -- : 
I, [2022-02-03T14:57:33.172648 #1]  INFO -- : > [ ! -e /shared/postgres_data ] && install -d -m 0755 -o postgres -g postgres /shared/postgres_data && sudo -E -u postgres /usr/lib/postgresql/13/bin/initdb -D /shared/postgres_data || exit 0
I, [2022-02-03T14:57:33.176143 #1]  INFO -- : 
I, [2022-02-03T14:57:33.176285 #1]  INFO -- : > chown -R postgres:postgres /shared/postgres_data
I, [2022-02-03T14:57:33.217533 #1]  INFO -- : 
I, [2022-02-03T14:57:33.217953 #1]  INFO -- : > chown -R postgres:postgres /var/run/postgresql
I, [2022-02-03T14:57:33.223177 #1]  INFO -- : 
I, [2022-02-03T14:57:33.223541 #1]  INFO -- : > /root/upgrade_postgres
I, [2022-02-03T14:57:33.234672 #1]  INFO -- : 
I, [2022-02-03T14:57:33.234986 #1]  INFO -- : > rm /root/upgrade_postgres
I, [2022-02-03T14:57:33.239178 #1]  INFO -- : 
I, [2022-02-03T14:57:33.240289 #1]  INFO -- : Replacing data_directory = '/var/lib/postgresql/13/main' with data_directory = '/shared/postgres_data' in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.241095 #1]  INFO -- : Replacing (?-mix:#?listen_addresses *=.*) with listen_addresses = '*' in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.242085 #1]  INFO -- : Replacing (?-mix:#?synchronous_commit *=.*) with synchronous_commit = $db_synchronous_commit in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.243048 #1]  INFO -- : Replacing (?-mix:#?shared_buffers *=.*) with shared_buffers = $db_shared_buffers in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.243702 #1]  INFO -- : Replacing (?-mix:#?work_mem *=.*) with work_mem = $db_work_mem in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.244275 #1]  INFO -- : Replacing (?-mix:#?default_text_search_config *=.*) with default_text_search_config = '$db_default_text_search_config' in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.245070 #1]  INFO -- : > install -d -m 0755 -o postgres -g postgres /shared/postgres_backup
I, [2022-02-03T14:57:33.253159 #1]  INFO -- : 
I, [2022-02-03T14:57:33.253713 #1]  INFO -- : Replacing (?-mix:#?checkpoint_segments *=.*) with checkpoint_segments = $db_checkpoint_segments in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.254012 #1]  INFO -- : Replacing (?-mix:#?logging_collector *=.*) with logging_collector = $db_logging_collector in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.254873 #1]  INFO -- : Replacing (?-mix:#?log_min_duration_statement *=.*) with log_min_duration_statement = $db_log_min_duration_statement in /etc/postgresql/13/main/postgresql.conf
I, [2022-02-03T14:57:33.255347 #1]  INFO -- : Replacing (?-mix:^#local +replication +postgres +peer$) with local replication postgres  peer in /etc/postgresql/13/main/pg_hba.conf
I, [2022-02-03T14:57:33.256179 #1]  INFO -- : Replacing (?-mix:^host.*all.*all.*127.*$) with host all all 0.0.0.0/0 md5 in /etc/postgresql/13/main/pg_hba.conf
I, [2022-02-03T14:57:33.256424 #1]  INFO -- : Replacing (?-mix:^host.*all.*all.*::1\/128.*$) with host all all ::/0 md5 in /etc/postgresql/13/main/pg_hba.conf
I, [2022-02-03T14:57:33.256612 #1]  INFO -- : > HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main
I, [2022-02-03T14:57:33.260200 #1]  INFO -- : > sleep 5
2022-02-03 14:57:33.386 UTC [44] LOG:  starting PostgreSQL 13.5 (Debian 13.5-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
2022-02-03 14:57:33.387 UTC [44] LOG:  listening on IPv4 address "0.0.0.0", port 5432
2022-02-03 14:57:33.387 UTC [44] LOG:  listening on IPv6 address "::", port 5432
2022-02-03 14:57:33.392 UTC [44] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2022-02-03 14:57:33.397 UTC [47] LOG:  database system was shut down at 2022-02-03 14:38:05 UTC
2022-02-03 14:57:33.423 UTC [44] LOG:  database system is ready to accept connections
I, [2022-02-03T14:57:38.264201 #1]  INFO -- : 
I, [2022-02-03T14:57:38.264624 #1]  INFO -- : > su postgres -c 'createdb discourse' || true
2022-02-03 14:57:38.386 UTC [57] postgres@postgres ERROR:  database "discourse" already exists
2022-02-03 14:57:38.386 UTC [57] postgres@postgres STATEMENT:  CREATE DATABASE discourse;
createdb: error: database creation failed: ERROR:  database "discourse" already exists
I, [2022-02-03T14:57:38.389331 #1]  INFO -- : 
I, [2022-02-03T14:57:38.389624 #1]  INFO -- : > su postgres -c 'psql discourse -c "create user discourse;"' || true
2022-02-03 14:57:38.549 UTC [61] postgres@discourse ERROR:  role "discourse" already exists
2022-02-03 14:57:38.549 UTC [61] postgres@discourse STATEMENT:  create user discourse;
ERROR:  role "discourse" already exists
I, [2022-02-03T14:57:38.551991 #1]  INFO -- : 
I, [2022-02-03T14:57:38.552249 #1]  INFO -- : > su postgres -c 'psql discourse -c "grant all privileges on database discourse to discourse;"' || true
I, [2022-02-03T14:57:38.711144 #1]  INFO -- : GRANT

I, [2022-02-03T14:57:38.711409 #1]  INFO -- : > su postgres -c 'psql discourse -c "alter schema public owner to discourse;"'
I, [2022-02-03T14:57:38.857756 #1]  INFO -- : ALTER SCHEMA

I, [2022-02-03T14:57:38.861781 #1]  INFO -- : > su postgres -c 'psql template1 -c "create extension if not exists hstore;"'
NOTICE:  extension "hstore" already exists, skipping
I, [2022-02-03T14:57:38.984634 #1]  INFO -- : CREATE EXTENSION

I, [2022-02-03T14:57:38.984942 #1]  INFO -- : > su postgres -c 'psql template1 -c "create extension if not exists pg_trgm;"'
NOTICE:  extension "pg_trgm" already exists, skipping
I, [2022-02-03T14:57:39.145788 #1]  INFO -- : CREATE EXTENSION

I, [2022-02-03T14:57:39.146097 #1]  INFO -- : > su postgres -c 'psql discourse -c "create extension if not exists hstore;"'
NOTICE:  extension "hstore" already exists, skipping
I, [2022-02-03T14:57:39.275806 #1]  INFO -- : CREATE EXTENSION

I, [2022-02-03T14:57:39.276090 #1]  INFO -- : > su postgres -c 'psql discourse -c "create extension if not exists pg_trgm;"'
NOTICE:  extension "pg_trgm" already exists, skipping
I, [2022-02-03T14:57:39.449636 #1]  INFO -- : CREATE EXTENSION

I, [2022-02-03T14:57:39.450035 #1]  INFO -- : > sudo -u postgres psql discourse
I, [2022-02-03T14:57:39.457694 #1]  INFO -- : update pg_database set encoding = pg_char_to_encoding('UTF8') where datname = 'discourse' AND encoding = pg_char_to_encoding('SQL_ASCII');

I, [2022-02-03T14:57:39.579072 #1]  INFO -- : File > /var/lib/postgresql/take-database-backup  chmod: +x  chown: postgres:postgres
I, [2022-02-03T14:57:39.584955 #1]  INFO -- : File > /var/spool/cron/crontabs/postgres  chmod:   chown: 
I, [2022-02-03T14:57:39.585144 #1]  INFO -- : > echo postgres installed!
I, [2022-02-03T14:57:39.590744 #1]  INFO -- : postgres installed!

I, [2022-02-03T14:57:39.591003 #1]  INFO -- : > sudo -u postgres createdb b_discourse || exit 0
2022-02-03 14:57:39.901 UTC [97] postgres@postgres LOG:  duration: 205.019 ms  statement: CREATE DATABASE b_discourse;
I, [2022-02-03T14:57:39.903962 #1]  INFO -- : 
I, [2022-02-03T14:57:39.904309 #1]  INFO -- : Terminating async processes
I, [2022-02-03T14:57:39.904358 #1]  INFO -- : Sending INT to HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid: 44
2022-02-03 14:57:39.904 UTC [44] LOG:  received fast shutdown request
2022-02-03 14:57:39.905 UTC [44] LOG:  aborting any active transactions
2022-02-03 14:57:39.920 UTC [44] LOG:  background worker "logical replication launcher" (PID 53) exited with exit code 1
2022-02-03 14:57:39.921 UTC [48] LOG:  shutting down
2022-02-03 14:57:39.949 UTC [44] LOG:  database system is shut down
/usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/config.rb:148:in `block (2 levels) in run_commands': Invalid run command stdin (SyntaxError)
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/config.rb:142:in `each'
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/config.rb:142:in `block in run_commands'
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/config.rb:141:in `each'
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/config.rb:141:in `run_commands'
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/config.rb:124:in `run'
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/lib/pups/cli.rb:65:in `run'
        from /usr/local/lib/ruby/gems/2.7.0/gems/pups-1.1.1/bin/pups:9:in `<top (required)>'
        from /usr/local/bin/pups:25:in `load'
        from /usr/local/bin/pups:25:in `<main>'
9c7809873c60b211188ae2853e971a5fd0fda903878c7dadaa89ab443efc3d77
** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one.
./discourse-doctor may help diagnose the problem.