Deploying a Discourse forum with Capistrano

[Updated] Here I describe how to automate deployment of Discourse to your own server using Capistrano.

In “How to host a Discourse forum on your own server” I went through the steps that are necessary to get Discourse up and running on my own server, rather than making use of Discourse’s hosted forums offering.

Capistrano is a remote server automation and deployment tool that very much facilitates deploying Ruby on Rails application. I personally find the documentation a bit sparse, but since I’ve previously managed to set it up for my own RoR web app already, it was not too difficult to adjust the configuration for the XL Toolbox Discourse forum.

# Capfile
# Load DSL and set up stages
require "capistrano/setup"

# Include default deployment tasks
require "capistrano/deploy"

# Load the SCM plugin appropriate to your project:
# require "capistrano/scm/hg"
# install_plugin Capistrano::SCM::Hg
# or
# require "capistrano/scm/svn"
# install_plugin Capistrano::SCM::Svn
# or
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

# Include tasks from other gems included in your Gemfile
# For documentation on these, see for example:
# require "capistrano/rvm"
require "capistrano/rbenv"
# require "capistrano/chruby"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require "capistrano/passenger"
require 'capistrano/sidekiq'

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

(Updated 2017-08-08: Added a require line for capistrano/sidekiq.)

In config/deploy.rb, I changed the linked_files directive so that config/discourse.conf is linked. Likewise, linked_dirswas modified to link public/uploads.

# config/deploy.rb
# config valid only for current version of Capistrano
lock "3.7.1"

set :application, "Discourse"
set :repo_url, ""

# Default branch is :master
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, "/var/discourse"

# Default value for :format is :airbrussh.
# set :format, :airbrussh

# You can configure the Airbrussh format using :format_options.
# These are the defaults.
# set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto

# Default value for :pty is false
# set :pty, true

# Default value for :linked_files is []
append :linked_files, "config/discourse.conf"

# Default value for linked_dirs is []
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system", "public/uploads"

# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }

# Default value for keep_releases is 5
# set :keep_releases, 5

set :rbenv_custom_path, '/home/daniel/.rbenv'
set :rbenv_type, :user
set :rbenv_ruby, '2.4.0'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} " +
    "RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}

Note that I added options for the rbenv plugin at the end of the file.

Finally, the production.rb file needs to be adjusted:

set :stage, :production
set :rails_env, :production
set :branch, 'master'
set :server_name, '<your server>'
server '', user: '<username>', roles: %w(web app db), primary: true

# Ensure the sidekiq and sidekiqctl commands can execute
set :rbenv_map_bins, %w(rake gem bundle ruby rails sidekiq sidekiqctl)

(Updated 2017-08-08: Added the last three lines to make it work with Sidekiq.)

Before deploying, make sure to create the uploads directory in the shared directory on the server, and also copy discourse.config to the shared directory. Otherwise, your first attempts to deploy will fail.

Note that the app will reside in a subdirectory called current, and it will no longer be a Git repository. Make sure to adjust the web/app server configuration to use the current subdirectory. And don’t worry about the missing Git repository. Discourse will issue warnings on the stdout and will no longer be able to determine the installed version, but I did not find any other problems with not running it from a Git repository.