rocketchat with mongo 5
Tags: mongodb, rocketchat
Published: 2021-10-14 21:07:44

rocketchat is a slick chat server that you can deploy yourself. if you're looking to include live chat in your next project or are just looking for a roll-your-own slack replacement, it's a good candidate.

in this fly-over we will be deploying rocket chat backed by mongodb 5.0 on an ec2 t2.micro running ubuntu 20.04 and we'll be putting it behind an nginx reverse proxy so we can serve easily over ssl.

## get an ec2 t2.micro
this is going to be on an ec2 t2.micro running ubuntu 20.04. for the detail-oriented, that's `ami-03d5c68bab01f3496`.

ensure that you have a public ip address. if you plan on this being used in production, an elastic ip is highly recommended.

the default disk size for t2.micros is 8gb. this is slim at the best of times, but rocket and mongo eat up disk space, so we're going to give ourselves a 20gb disk.

we will also want to have a domain name pointing to our ec2, so fire up route53 and do that, and port 3000 open on our firewall. we will close this port later.

## set some swap space
ec2 micros only come with one gig of ram out of the box and no swap partition, which is fine for hello-world calibre stuff, but rocketchat wants ram. so, first thing is to get a swapfile up and running.
```bash
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
```
here we create an empty file 2 gigs in size using `fallocate`, lock its permissions down then deploy it as memory swap. you can see the effects immediately with `free -h`.

obviously, we want our swap file to persist after reboot, so will add it to the 'file system table' (called 'fstab'). open up `/etc/fstab` as root in your favourite text editor and drop this line at the bottom:
```bash
/swapfile none swap sw 0 0
```

## install some prerequisites
there are some necessary prerequisites for rocket and mongo. the big one here is `build-essentials` which includes `gcc` and `make` and other similar tools.
```bash
sudo apt update
sudo apt install curl build-essential graphicsmagick
```

## install node 12
at the time of writing, the latest version of rocketchat is 4.02. this requires `node` 12.22.1, so we will install the latest node from 12.x.
```bash
curl -sL 'https://deb.nodesource.com/setup_12.x' | sudo bash -
sudo apt update
sudo apt install nodejs
```

once done you can check with 
```bash
node --version
```

## install mongodb 5.0
the official docs say rocketchat 4.02 works with mongodb 4.0. however, on first run you will prompted to upgrade mongo. we _could_ do that, or we could just install mongo 5.0 right away.

mongo is quite demanding of system resources, so we will need to be increasing our ec2's system limits later if mongo is going to run, but first, we will do the install
```bash
wget -qO - <https://www.mongodb.org/static/pgp/server-5.0.asc> | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] <https://repo.mongodb.org/apt/ubuntu> focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list
sudo apt update
sudo apt install mongodb-org
```

mongo is installed from a custom source, so here we load the gpg key and create the necessary source file. then it is a simple matter of update and install.

once we have mongodb installed, there's some simple configuration
```bash
sudo sed -i "s/^#  engine:/  engine: wiredTiger/"  /etc/mongod.conf
sudo sed -i "s/^#replication:/replication:\n  replSetName: rs01/" /etc/mongod.conf
```

if you look at the official rocketchat docs, you will notice that the mongo engine they specify is `mmapv1`. that's deprecated in mongodb 5.0, so we will be using `wildTiger` instead.

as previously mentioned, mongo needs an increase in our ec2's system limits if it's going to run well (or at all). most notably, we're going to be bumping up the maximum number of files usable by the `mongodb` user. 

first, we will increase our file-max. as root, using the editor of your choice, open `/etc/sysctl.conf` and paste this at the bottom of the file.
```
fs.file-max = 200000
```

then run
```bash
sudo sysctl -p
```

next, we're going to modify the soft and hard per-user limits for the user that runs mongodb. for this we will edit, as root, the file `/etc/security/limits.conf` and add, at the bottom:
```
mongodb soft     nproc          200000
mongodb hard     nproc          200000
mongodb soft     nofile         200000
mongodb hard     nofile         200000
mongodb soft     memlock        2048
mongodb hard     memlock        2048
```

that's a large "number of files" (nofile) and "number of processes" (noproc)!

then, we will update the pluggable authentication module (pam) to actually read these limits. to do this, edit, as root, `/etc/pam.d/common-session` and add, at the bottom:
```
session required pam_limits.so
```

and that's it. we have given the user that runs mongo expanded resource access so our db doesn't fall over on start.

let's start up the mongodb daemon.
```bash
sudo systemctl enable mongod.service
sudo systemctl restart mongod.service
```

to see the product of our success, we can check its status
```bash
sudo systemctl status mongod.service
```

## install rocketchat
at long last, we are actually ready to install rocket chat.

there are a few steps to this process:
* download the source tarball
* install the dependencies with npm
* put the executable where we want it
* create a user to run rocketchat, and then
* configure systemd so we can run it as a daemon

### download and install dependencies
first we're going to download the 'latest' version of rocketchat (4.0.2 at this writing) and untar it.
```bash
curl -L https://releases.rocket.chat/latest/download -o /tmp/rocket.chat.tgz
cd /tmp/
tar xzf rocket.chat.tgz
```

now that we have the source on disk, we install the dependencies.
```bash
cd /tmp/bundle/programs/server
npm install
```
this, of course, will take some time.

### put the executable where we want it
the offical docs imply that the executable should live in `/opt`. many people much smarter than i have waged war over `/opt` vs. `/usr/local/bin` and, while i do have opinions, it is probably better to stick with the 'canonical' decision, if for no other reason than to make the official documentation easier to use. 'opt' it is
```bash
sudo mv /tmp/bundle /opt/rocket.chat
```

### create a user to run rocketchat
here we create user and group 'rocketchat' and then give that user ownership of our rocketchat executable. it is certainly possible to run rocketchat as the 'ubuntu' user, but it is not a good idea. let's make the user, group and assign ownership of the executable:
```bash
sudo useradd -M rocketchat 
sudo usermod -L rocketchat
sudo chown -R rocketchat:rocketchat /opt/rocket.chat
```

### daemonize it
rocketchat is a service in the same way that, say, `nginx` is, so we want to be able to run it as a daemon in the same way. doing this involves creating a fairly straight forward `systemd` configuration file.

as root, create and edit the file `/lib/systemd/system/rocketchat.service`

in that file paste:
```
[Unit]
Description=The Rocket.Chat server
After=network.target remote-fs.target nss-lookup.target nginx.service mongod.service
[Service]
ExecStart=/usr/bin/node /opt/rocket.chat/main.js
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=rocketchat
User=rocketchat
Environment=MONGO_URL=mongodb://localhost:27017/rocketchat?replicaSet=rs01 MONGO_OPLOG_URL=mongodb://localhost:27017/local?replicaSet=rs01 ROOT_URL=<PUBLIC_URL> PORT=3000
[Install]
WantedBy=multi-user.target
```

*don't save yet!*
there are things you will need to change:

* *PUBLIC_URL*: this is on the `Environment` line. you will need to put in the url you intend to serve rocketchat on here and specify the port 3000. ie. `http://rocket.example.ca:3000`. note that this is `http` _not_ `https`, and don't forget to add the `:3000` port at the end.
* *Description*: it's not mandatory to change this, but it's nice to have something more personal than the factory defaults

you may also want to confirm that your `node` bin actually lives in `/usr/bin/node` and edit accordingly.

once you have made the necessary changes, save your systemd file.

### final steps
we're very close to being able to start rocketchat, but first we need to start mongodb replicating with:
```bash
mongo --eval "printjson(rs.initiate())"
```

now all we have to do is enable and start the rocketchat daemon:
```bash
sudo systemctl enable rocketchat
sudo systemctl start rocketchat
```

to confirm that we're running on all four wheels, check the status:
```bash
sudo systemctl status rocketchat
```

## configure rocketchat
now that rocketchat is up and running and serving on port 3000, we can go and edit all of its configurations using the web interface. 

navigate to the url you set as `ROOT_URL` above, ie `http://rocket.example.ca:3000` and follow the instructions.

this is also an excellent time to discover if you have port 3000 open on your firewall!

## proxy for ssl
we're successfully serving rocketchat now, but it's not... satisfying. obviously we will want our chat to use ssl and use the standard 443 port, not 3000. to accomplish this, we're going to put together a proxy with nginx.

### install nginx
we're going to use nginx for proxying, so the first step is to install it:
```bash
sudo apt update
sudo apt install nginx
```

easy!

### configure nginx
this is a little harder. using the editor of your choice, as root, create and edit the file `etc/nginx/sites-available/001-rocketchat.conf`

then add to it:
```
# Upstreams
upstream backend {
    server 127.0.0.1:3000;
}

# HTTPS Server
server {
    listen 80;

    server_name your_hostname.com;

    # You can increase the limit if your need to.
    client_max_body_size 200M;

    error_log /var/log/nginx/rocketchat.access.log;

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Nginx-Proxy true;

        proxy_redirect off;
    }
}
```
*don't save yet!*
you will see on line 10 `server_name your_hostname.com;`. change `your_hostname.com` to the hostname you set in `ROOT_URL` minus the port, ie `rocket.example.ca`. now save

now we are going to enable this nginx configuration by making a symlink in `sites-enabled`:
```bash
cd /etc/nginx/sites-enabled/
sudo ln -s ../sites-available/001-rocketchat.conf
```

we can confirm that our configuration is good thusly:
```bash
sudo nginx -t
```
you should see an 'Ok' message.

once we have a good configuration, we restart nginx and begin serving:
```bash
sudo systemctl restart nginx.service
```

### get ssl
we're now serving through our proxy on old-fashioned http, but we would really prefer to be using ssl.

to accomplish this, we're going to use the EFF's `certbot` tool to get a free signed certificate and install it.

first, we install the `certbot` tool. this is delivered as a snap:
```bash
sudo snap install core; sudo snap refresh core
sudo snap install certbot --classic
```

once certbot is installed, getting a signed certificate is simple.
```bash
sudo certbot --nginx
```
we will be asked for our email and to accept the terms and conditions, and then prompted to choose which site we want to install ssl for. we only have one site, so that's easy!

once `certbot` is done, we can inspect, if we choose, our nginx configuration at `etc/nginx/sites-available/001-rocketchat.conf` and see the changes `certbot` has made.

certbot certificates are only good for three months. fortunately, it is straightforward to set up auto-renewal. we'll do that with:
```bash
sudo certbot renew --dry-run
```

## enjoy chatting
that's it. we can now navigate to our rocketchat install at our `ROOT_URL` on https, ie `https://rocket.example.ca` and troll whoever is there.