Premise

On this blog, I had commenting enabled through the free tier of Disqus. It worked well, however, I noticed that a bunch of trackers tagged along. Especially, facebook.net, among other things. I did not find a way to turn Twitter Like and Share buttons either, and the paid subscription I briefly considered promised to provide the ability to hide them. Hide. Not remove. So, goodbye Disqus.

Edit: I later returned the Discus, but now the user needs to actively click on the link to load comments and expose themselves to third-party tracking.

What now? I like Discourse a lot. It’s ridiculously feature-reach free and opensource forum software, so installing it on some cloud instance was a no-brainer. I have considered a few cloud options, and because I don’t want this blog to cost me more money I was mainly focused on low-cost or free cloud hosting options – such as Digital Ocean Droplets and GCP Free tier.

Comparing specs side by side it appears that Oracle’s always-free tier provides sufficiently beefy virtual instance, so I went with that. In addition, I always wanted to play with Oracle Linux so that was a plus.

So why this blog?

It all seemed straightforward, with tons of documentation online; why write one more? Because as usual nothing worked out of the box and I had to do a few tweaks here and there. Consider these to be more of notes for myself than anything with the side benefit of potentially helping someone else.

Cloud VM Instance

Create

There is a tutorial on oracle blogs but it seems the UI has changed somewhat and its hard to follow it.

  • Create a free account here: https://www.oracle.com/cloud/free/
  • at https://cloud.oracle.com select region. I picked US West (Phoenix)
  • Click on a huge Create a VM Instance box in Quick Actions. Note Always Free Eligible badge
  • Pick AMD VM.Standard.E2.1.Micro in Availability Domain AD-1 shaped VM.Standard.E2.1.Micro. This provides an always-free instance with 1 OCPU with 1 GB RAM and 0.48 Gbps network bandwidth. The names and domains might change but the key is to pick Always-Free eligible objects.
  • Scroll down and set Assign a public IPv4 address: Yes
  • Download a pair of SSH keys. (Or create a custom pair and upload public key). Set permissions to your private key 600: chmod 600 ssh-key.key.
  • Create the machine and wait for it to become available.
  • Copy the public IP address and add it to the A record on your DNS provider with the appropriate subdomain name. I picked forum.arrogantrabbit.com. This will take some time to propagate, so it makes sense to do it early.
  • From the instance details page click on the subnet link; there click on Default Security List and click Add Ingress Rules. Add this:
    • source type: CIDR
    • source CIDR: 0.0.0.0/0 (you may want to change that later if you want to only allow your proxy)
    • IP Protocol: TCP
    • Source Port Range: All
    • Destination Port Range: 80,443

Configure

  • login to your newly created instance. Note the username is opc. If you forget, the banner will remind you.
      ssh -i ssh-key.key [email protected]
    
  • Add firewall rules
      sudo firewall-cmd --permanent --zone=public --add-port=80/tcp
      sudo firewall-cmd --permanent --zone=public --add-port=443/tcp
      sudo firewall-cmd —reload
    
  • Install prerequisites
      yum-config-manager --enable ol7_addons
      sudo yum install git nc docker-engine -y
      systemctl start docker
      systemctl enable docker
    
  • There is a way to use Oracle’s email gateway for outgoing emails (Hamburger | Email Delivery | Email Configuration), but I already had Amazon SES configured, so I was going to use that.

Discourse

Install

Download it first:

sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse

Before you proceed you would need to apply a dirty hack to override hardware detection in the install script. For some reason CPU count and memory size detection returns 0 and instead of debugging the script, it’s much easier to just hardcode data we already know about our instance.

diff --git a/discourse-setup b/discourse-setup
index b4472a6..f5dca47 100755
--- a/discourse-setup
+++ b/discourse-setup
@@ -221,6 +221,11 @@ scale_ram_and_cpu() {
     avail_gb=$(check_linux_memory)
     avail_cores=$((`awk '/cpu cores/ {print $4;exit}' /proc/cpuinfo`*`sort /proc/cpuinfo | uniq | grep -c "physical id"`))
   fi
+
+  # Dirty hack to workaround broken detection on Oracle Linux
+  avail_gb=1
+  avail_cores=1
+
   echo "Found ${avail_gb}GB of memory and $avail_cores physical CPU cores"

   # db_shared_buffers: 128MB for 1GB, 256MB for 2GB, or 256MB * GB, max 4096MB

Save this into e.g. patch.diff in /var/discourse folder and apply patch -p1 < patch.diff.

Now proceed with the install as usual.

./discourse-setup

Follow the official guide for the rest of configuring the instance, including auto-updates, reporting, and backups.

Migrate comments

Discourse includes a script to do that, however, it seems to be broken as of today: Disqus does not export email addresses for users, and the migration script faceplants when the email is empty.

So, follow this https://meta.discourse.org/t/migrating-to-discourse-from-another-forum-software/16616 to set up development environment on your local machine, then export backup from your cloud instance and restore your local environment with it.

This assumes you have the working ruby environment on your machine. if not - install it through homebrew:

brew install rbenv ruby-build
rbenv init

Then set the local environment to whatever you want: rbenv local 2.7.1.

Export your comments history from disqus, but before running migration scrip to import the file apply the following patch:

diff --git a/script/import_scripts/disqus.rb b/script/import_scripts/disqus.rb
index 5dbeb08775..5fd1621d1b 100644
--- a/script/import_scripts/disqus.rb
+++ b/script/import_scripts/disqus.rb
@@ -35,11 +35,15 @@ class ImportScripts::Disqus < ImportScripts::Base
     by_email = {}

     @parser.posts.each do |id, p|
+      p[:author_email] = "#{p[:author_username]}@disqus.invalid" unless p[:author_usrname]
       next if p[:is_spam] == 'true' || p[:is_deleted] == 'true'
+      puts "name: #{p[:author_name]}, username: #{p[:author_username]}, email: #{p[:author_email]} "
       by_email[p[:author_email]] = { name: p[:author_name], username: p[:author_username] }
     end

-    @parser.threads.each do |id, t|
+     @parser.threads.each do |id, t|
+      t[:author_username] = "#{t[:author_name]}" unless t[:author_username]
+      t[:author_email] = "#{t[:author_username]}@disqus.invalid" unless t[:author_email]
       by_email[t[:author_email]] = { name: t[:author_name], username: t[:author_username] }
     end

Then modify the information up top (path to XML file exported from Disqus and category to apply to the posts) and run

bundle exec ruby script/import_scripts/disqus.rb

Now save the backup, download it, upload to your cloud instance and restore there. Great.

Embedding

For embedding: go to https://your.forum.example.com/admin/customize/embedding – substitute with your forum URL, and do the following (each step important)

  • provide FQDN of allowed host and category where to create discussions
  • Specify the username for topic creation
  • Disable Truncate the embedded posts
  • Enable making topics unlisted until there is a reply.

Then follow the embedding instructions on that page: insert the snippet into _layouts\post.html of your Jekyll blog source, right next to the Disqus piece, and disable Disqus in your _config.yml.

Summary

The resulting instance is not the fastest, especially when logging in as admin, but it’s sufficient to host comments for low traffic sites like this one, and at least the goal of shielding my readers from third-party trackers has been accomplished.