Deploying RocketChat using Docker Compose on Ubuntu 18.04

Boxing the Rocket

Deploying RocketChat using Docker Compose on Ubuntu 18.04

Boxing the Rocket

Introduction

This article is about installing and deploying RocketChat. And then specifically in the following configuration: I wanted to use RocketChat in Docker containers (using Docker Compose) on a TransIP BladeVPS with Ubuntu 18.04. Sounds simple, right?

There are multiple references for deploying RocketChat, but none in the immediate configuration that I wanted. The closest was the DigitalOcean tutorial on how to install, configure and deploy RocketChat on Ubuntu 14.04. But that was partly outdated (upstart is not supported anymore) and not using Docker.

I ran into a lot of issues using the standard documentation. Most issues required me to cobble together snippets and advice from more than 30 different pages. Anyway.. I succeeded.. And wanted to help others that are looking for a similar configuration. So here goes.

Safety First

We’ll assume you already have a host (I’m using a TransIP BladeVPS X1) running a fresh Ubuntu 18.04 installation.

We’ll start by configuring a tight firewall and safeguards.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

We also want to make sure that brute-forcing attempts on SSH will be blacklisted. Fail2ban was made for that, so let’s install that as well.

sudo apt-get update
sudo apt-get install fail2ban

And just for correctness, we need to make sure our FQDN (Fully Qualified Domain Name) is also in /etc/hosts. In our case we are using chat.xyz.com.

127.0.0.1   localhost
127.0.0.1   chat.xyz.com    chat

Our reverse proxy - nginx

We want to only serve HTTPS using nginx as a reverse proxy. For SSL we will be using the Let’s Encrypt service. The EFF developed a tool for that, called Certbot. Because Certbot is always in heavy development, we want to have the latest version (and will be using a different repository).

sudo apt-get install nginx software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get install python-certbot-nginx

Now we need to setup our nginx config to provide a secure reverse proxy for our server. So we will replace the default nginx configuration. I use sudo vi /etc/nginx/sites-available/default, but if you are not familiar with vi, I suggest you use sudo nano /etc/nginx/sites-available/default instead (Don’t forget to replace chat.xyz.com with your own domain name).

upstream rocketchat_backend {
  server 127.0.0.1:3000;
}

server {
    listen 443 default_server ssl;
    server_name chat.xyz.com;

    error_log /var/log/nginx/rocketchat_error.log;

    ssl_certificate /etc/nginx/certificate.crt;
    ssl_certificate_key /etc/nginx/certificate.key;
    ssl_dhparam /etc/nginx/dhparams.pem;
    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA512:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:DH+AESGCM:DH+AES256:RSA+AESGCM:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 180m;

    location / {
        proxy_pass http://rocketchat_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-Forward-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forward-Proto http;
        proxy_set_header X-Nginx-Proxy true;

        proxy_redirect off;
    }
}

Don’t worry about the fact we are using the ‘default certificate settings’ for now. Otherwise nginx won’t start. So we’ll generate a base cert for now. You only need to enter your CN (Common Name) correctly as ‘chat.xyz.com’.

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/certificate.key -out /etc/nginx/certificate.crt
sudo openssl dhparam -out /etc/nginx/dhparams.pem 2048

This generates an insecure self-signed certificate, but it’s enough to let nginx start up. Now we can test our config, and if all works we can have Let’s Encrypt do the rest.

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

$ sudo systemctl reload nginx

Let’s Encrypt to the rescue

We have to temporarily allow port 80 for Certbot to do it’s work:

sudo ufw allow 80/tcp

Now we can have Certbot get us our certificate and then deny port again:

sudo certbot --nginx -d chat.xyz.com
sudo ufw delete allow 80/tcp
sudo ufw status

Installing RocketChat

Now we can start with the installation of our core reason for being here: RocketChat!

Let’s prepare the base directory structure first:

sudo mkdir -p /var/www/rocket.chat/data/runtime/db
sudo mkdir -p /var/www/rocket.chat/data/dump

For our Docker Compose environment to work we need to provide it with a YAML configuration in /var/www/rocket.chat/docker-compose.yml (Again don’t forget to replace chat.xyz.com).

version: "3.3"
services:
  db:
    image: mongo
    volumes:
      - ./data/runtime/db:/data/db
      - ./data/dump:/dump
    command: mongod --smallfiles

  rocketchat:
    image: rocketchat/rocket.chat:latest
    environment:
      - MONGO_URL=mongodb://db:27017/rocketchat
      - ROOT_URL=https://chat.xyz.com
      - Accounts_UseDNSDomainCheck=False
    links:
      - db:db
    ports:
      - "3000:3000"
    depends_on:
      - db

Security

We don’t want to run under root, so let’s prepare a special user for running RocketChat (and make it part of the docker group).

sudo useradd -m -U -r -d /var/www/rocket.chat rocket
sudo usermod -a -G docker rocket
sudo chown -R rocket.rocket /var/www/rocket.chat/

Launching Mongo

Ok.. Let’s rock! We can now try to start the Mongo container as the rocket user:

sudo su -l -c "docker-compose up db" rocket

If it starts up correctly, we can migrate it to a daemon, by pressing CTRL-C and then running:

sudo su -l -c "docker-compose up -d db" rocket

Launching RocketChat

Now we do the same for RocketChat:

sudo su -l -c "docker-compose up rocketchat" rocket

It takes some time to start, and then you should see something like this:

rocketchat_1  | ➔ +---------------------------------------------------------+
rocketchat_1  | ➔ |                      SERVER RUNNING                     |
rocketchat_1  | ➔ +---------------------------------------------------------+
rocketchat_1  | ➔ |                                                         |
rocketchat_1  | ➔ |  Rocket.Chat Version: 0.74.3                            |
rocketchat_1  | ➔ |       NodeJS Version: 8.11.4 - x64                      |
rocketchat_1  | ➔ |             Platform: linux                             |
rocketchat_1  | ➔ |         Process Port: 3000                              |
rocketchat_1  | ➔ |             Site URL: https://chat.xyz.com              |
rocketchat_1  | ➔ |     ReplicaSet OpLog: Disabled                          |
rocketchat_1  | ➔ |          Commit Hash: 202a465f1c                        |
rocketchat_1  | ➔ |        Commit Branch: HEAD                              |
rocketchat_1  | ➔ |                                                         |
rocketchat_1  | ➔ +---------------------------------------------------------+

Then we can quit the rocketchat instance by pressing CTRL-C, and start it for real:

sudo su -l -c "docker-compose up -d rocketchat" rocket

Starting and restarting

Of course we want our RocketChat server to start when the server starts, and restart whenever it crashes. Ubuntu 18.04 is using systemd as its main agent, so we’ll use that. I explicitly do not use the docker-compose restart config variable as the app should wait a bit for the database. Just using depends_on is not enough.

So to make sure our Mongo docker spins up first, we’ll make /etc/systemd/system/rocketchat_mongo.service:

[Unit]
Description=MongoDB service for RocketChat
After=docker.service
Requires=docker.service

[Service]
User=rocket
Group=rocket
TimeoutStartSec=0
RestartSec=10
Restart=always
WorkingDirectory=/var/www/rocket.chat
ExecStartPre=-/usr/bin/docker-compose stop db
ExecStart=/usr/bin/docker-compose up db
ExecStop=/usr/bin/docker-compose stop db

[Install]
WantedBy=multi-user.target

And then to start up the main RocketChat Docker 10 seconds later we’ll make /etc/systemd/system/rocketchat_app.service:

[Unit]
Description=RocketChat
After=rocketchat_mongo.service
Requires=rocketchat_mongo.service

[Service]
User=rocket
Group=rocket
TimeoutStartSec=0
RestartSec=10
Restart=always
WorkingDirectory=/var/www/rocket.chat
ExecStartPre=-/usr/bin/docker-compose stop rocketchat
ExecStartPre=/bin/sleep 10
ExecStart=/usr/bin/docker-compose up rocketchat
ExecStop=/usr/bin/docker-compose stop rocketchat

[Install]
WantedBy=multi-user.target

We can enable both for reboot by typing:

systemctl enable rocketchat_mongo
systemctl enable rocketchat_app

And after a reboot our server will just come up.


comments powered by Disqus