six demon bag

Wind, fire, all that kind of thing!

2020-06-11

Installing Discourse on Devuan

Before deciding on setting up my Q&A site with Question2Answer I evaluated several programs, one of them being Discourse. And even though I ultimately decided against Discourse (because the setup was too complex and it doesn't have all the features I wanted) I don't want my experiences go to waste, so I'll publish them here.


The setup basically consists of 5 parts:

  • Ruby
  • PostgreSQL (for persistent data)
  • Redis (for volatile data, like scheduled jobs, sessions, ...)
  • Discourse (duh)
  • Nginx (or another web server that can reverse-proxy connections)

I started with this guide, because I think Docker (the official deployment format for Discourse) is a poor choice for any stateful service. However, I had to make several adjustments to make things work.

Ruby

The first thing you need for a successful Discourse setup is a recent enough Ruby interpreter. Since the one shipped with Debian-based systems like Devuan is too old you need to manually install a more recent version. There are several ways to do that, but I chose the rvm approach for simplicity (even though that's a relative term for anything Ruby).

First download the installer for rvm:

apt-get install dirmngr
gpg --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io -o rvm-installer
chmod a+x rvm-installer

After you verified that the script rvm-installer won't do anything nasty on your system (never run curl | bash, no matter how convenient that may seem) run the following command to install rvm:

./rvm-installer stable

Then use rvm to install Ruby:

. /etc/profile.d/rvm.sh
rvm install ruby
rvm --default use ruby

If you want you can also generate the Ruby documentation:

rvm docs generate-ri

PostgreSQL

Install PostgreSQL. The package postgresql-contrib is required for the hstore and pg_trgm extensions. The dev package is needed for the Discourse setup later on. To improve security you should require authentication even for local logins (see line 3).

pg_version='9.6'
apt-get install postgresql postgresql-contrib postgresql-server-dev-${pg_version}
sed -i -e 's/^\(local\s\+all\s\+all\s*\)$/\1 md5/' /etc/postgresql/${pg_version}/main/pg_hba.conf
service postgresql restart

Create a database user and database for Discourse:

su - postgres -c 'createuser --encrypted --pwprompt discourse'
su - postgres -c 'createdb --owner discourse discourse'
su - postgres -c "psql -c 'CREATE EXTENSION hstore; CREATE EXTENSION pg_trgm;' discourse"

Write down the password you specified when creating the database user as you need to put that into the Discourse config later on.

Redis

You need the package redis-server from backports, because the version that ships with Devuan Ascii is too old for Sidekiq.

cat <<EOF >/etc/apt/preferences.d/redis.pref
Package: redis-*
Pin: release a=stable-backports
Pin-Priority: 1000
EOF
apt-get install redis-server

Discourse

Create a service account so you can run Discourse as a dedicated user:

adduser --system --group --home /srv/discourse --shell /bin/bash --gecos 'Discourse Service User' --disabled-password discourse

You'll need a Git client, and Discourse also requires some helper applications:

apt-get install git brotli imagemagick

Clone the Discourse Git repository and check out the desired version (at the time I evaluated the software the stable version was 2.4.4).

git clone https://github.com/discourse/discourse.git /srv/discourse
git checkout 'v2.4.4'

Create the subdirectories sockets and pids in /srv/discourse/tmp, since they won't be created automatically and their absence would cause some breakage.

mkdir -p /srv/discourse/tmp/sockets
mkdir -p /srv/discourse/tmp/pids

Now you're ready to actually set up Discourse.

cd /srv/discourse
RAILS_ENV=production bundle install
cp config/discourse_defaults.conf config/discourse.conf
chown -R discourse: /srv/discourse

Edit /srv/discourse/config/discourse.conf. At the very least you need to fill in the following parameters (replace the highlighted values with the appropriate values for your environment; the database password is the one you specified when creating the database user):

db_host = localhost
db_username = discourse
db_password = PASSWORD
hostname = 'dis.example.org'
smtp_address = localhost
smtp_domain = 'example.org'
smtp_enable_start_tls = false

Note: The mail settings assume that you have a local mail server (like Postfix) or a local mail relay (like sSMTP) to accept and forward mail. If your mail setup is different from that you need to adjust the mail parameters accordingly.

Become the user "discourse" (su - discourse) and run the following command to prime the database, precompile the Discourse assets (you need to fix the brotli commandline parameters first, though), and create an admin user:

RAILS_ENV=production bundle exec rake db:migrate

sed -i -e '/brotli -f/ s/=/ /g; s/\(quality}\) \(#{path}\)/\1 --input \2/' lib/tasks/assets.rake
RAILS_ENV=production bundle exec rake assets:precompile

RAILS_ENV=production bundle exec rake admin:create

Discourse will be started via Puma, so you need to adjust the application path in the script puma.rb:

sed -i -e 's|\(APP_ROOT = \).*|\1"/srv/discourse"|' config/puma.rb

Also adjust the Sidekiq configuration:

cat <<EOF > config/sidekiq.yml
---
:verbose: false
:concurrency: 5
:timeout: 25
:optipng: false
:jhead: false
:jpegoptim: false
:gifsicle: false
:svgo: false
development:
  :concurrency: 5
  :queues:
    - [critical, 8]
    - [default, 4]
    - [low, 2]
    - [ultra_low]
production:
  :concurrency: 20
  :queues:
    - [critical, 8]
    - [default, 4]
    - [low, 2]
    - [ultra_low]
EOF

Note: I decided to disable all image optimizations. If you want to use any of them you need to install the respective tool(s) and set their option in sidekiq.yml to true.

Lastly, become root again and create init scripts for Discourse and Sidekiq. Remember, this is Devuan; we don't have no stinkin' Systemd here! Now enable and start the services:

curl -sOJ https://www.planetcobalt.net/download/discourse_initscripts.tar.gz
tar xzf discourse_initscripts.tar.gz -C /etc/init.d

cat <<EOF >/etc/default/discourse
APP_ENV='production'
EOF
chmod a+x /etc/init.d/discourse
update-rc.d -f discourse defaults
service discourse start

cat <<EOF >/etc/default/sidekiq-discourse
APP_ENV='production'
EOF
chmod a+x /etc/init.d/sidekiq-discourse
update-rc.d -f sidekiq-discourse defaults
service sidekiq-discourse start

Nginx

At this point Discourse is running and listening on localhost. We'll be using Nginx to reverse-proxy connections to the application service to make it accessible publicly. The Nginx sample config that ships with Discourse is a good starting point, so we'll generate the initial virtual host config from it (replace dis.example.org with the hostname you specified in discourse.conf). The brotli compression option is commented out because Nginx on Devuan doesn't support that algorithm yet.

apt-get install nginx
sed \
  -e '/^upstream/,/^}/d' \
  -e '/^# upstream/,/^# }/ s/^# //' \
  -e 's/\(server_name\) .*/\1 dis.example.org;/' \
  -e 's|/var/www/discourse|/srv/discourse|' \
  -e 's|/var/nginx/cache|/var/cache/nginx|' \
  -e 's/brotli/#&/' \
  config/nginx.sample.conf > /etc/nginx/sites-available/discourse.conf

Edit discourse.conf to make further adjustments as needed. I recommend at least enabling SSL. Once that is done you can enable the virtual host and restart Nginx. I'd suggest to also remove the default config.

ln -s /etc/nginx/sites-available/discourse.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
service nginx restart

Note: Beware that name resolution for the hostname you picked must work at this point, otherwise you'll get a blank page instead of a login dialog when trying to log into your new Discourse.

Navigate to your Discourse site, log in as the admin user, and complete the application configuration.

Posted 19:57 [permalink]