Using docker-compose to quickly deploy Rocket.Chat

Rocket.Chat is an open-source business communication platform that provides many features such as persistent chat rooms (channels) organised by topic, private groups, and direct messaging. The platform can be self-hosted.

In this blog post I will detail the steps I used to quickly and reliably deploy Rocket.Chat platform on a Linux machine running Docker.

Rocket.Chat provides an official Docker image hosted on the public Docker registry under: https://hub.docker.com/r/rocketchat/rocket.chat. Rocket.Chat also hosts their images on their own publicly accessible registry that can be found under: registry.rocket.chat/rocketchat/rocket.chat:latest.

In fact, Rocket.Chat provide a docker-compose.yaml example file . Sadly at the time of writing this blog entry, the docker-compose file was outdated and needed modifications especially changes necessary for using the latest version of MongoDB, the database that Rocket.Chat uses.

The docker-compose.yaml file displayed below defines five services:

  1. rocketchat: the webapp itself
  2. mongo: the mongodb nosql database which in this configuration is running on the same server
  3. mongo-init-replica: that will run only once and create a replica set of the mongo database used by the platform
  4. nginx: the http server configured to use a Let’s Encrypt SSL Certificate using certbot
  5. certbot: a service to renew the SSL certificate automatically

The following setup assumes an SSL certificate was already obtained, and that the configuration and auxiliary files are in the correct places

compose.yaml 
version: "3"
services:
  rocketchat:
    container_name: rocketchat
    image: rocket.chat:latest
    restart: unless-stopped
    environment:
     - PORT=3000
     - ROOT_URL=https://mywebsite.com
     - MONGO_URL=mongodb://mongo:27017/rocketchat
     - MONGO_OPLOG_URL=mongodb://mongo:27017/local
    depends_on:
     - mongo
  mongo:
    container_name: mongo
    image: mongo:latest
    restart: unless-stopped
    volumes:
     - ./data/db:/data/db
    command: mongod --oplogSize 128 --replSet rs0 
    labels:
      - "traefik.enable=false"

  # this container's job is just run the command to initialize the replica set.
  # it will run the command and remove himself (it will not stay running)
  mongo-init-replica:
    image: mongo:latest
    command: >
      bash -c
        "for i in `seq 1 30`; do
            msg=$$(mongosh mongo/rocketchat --quiet --eval \"
            rs.initiate({
              _id: 'rs0',
              members: [ { _id: 0, host: 'mongo:27017' } ]})\" 2>&1)
            s=$$?
            if [ \"$$s\" -eq 0 ]; then
              break
            fi
            if [[ \"$$msg\" == \"MongoServerError: already initialized\" ]] ; then
              echo $$msg
              break
            fi
          echo \"Tried $$i times. Waiting 5 secs...\";
          sleep 5;
        done; (exit 0)"
    depends_on:
      - mongo

  nginx:
    container_name: nginx
    image: nginx:latest
    restart: unless-stopped
    environment:
      - DOMAIN
    depends_on:
      - rocketchat 
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./etc/nginx/templates:/etc/nginx/templates:ro
      - ./etc/letsencrypt:/etc/letsencrypt:ro
      - ./certbot/data:/var/www/certbot
  certbot:
    container_name: certbot
    image: certbot/certbot:latest
    depends_on:
      - nginx
    command: >-
             certonly --reinstall --webroot --webroot-path=/var/www/certbot
             --email ${EMAIL} --agree-tos --no-eff-email
             -d ${DOMAIN}
    volumes:
      - ./etc/letsencrypt:/etc/letsencrypt
      - ./certbot/data:/var/www/certbot

Here is a tree view of the folder structure, configuration and auxiliary files. for presentation purposees the content of the letseencrypt folder are not shown.

.
├── docker-compose.yaml
├── .env
└── etc
    ├── letsencrypt
    └── nginx
        └── templates
            └── default.conf.template

and the content of the NGINX configuration file:

server {
    listen [::]:80;
    listen 80;
    server_name $DOMAIN;
    return 301 https://$host$request_uri;
}
 
server {
    listen [::]:443 ssl http2;
    listen 443 ssl http2;
    server_name $DOMAIN; 
    ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

    client_max_body_size 200M;
 
    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Host $host;
      proxy_set_header X-Forwarded-Proto https;
      proxy_set_header X-Nginx-Proxy true;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_http_version 1.1;
      proxy_redirect off;
      proxy_pass http://rocketchat:3000;
  }
}

Final words

Docker and docker-compose has made it possible to deploy and administer web-apps on heterogenous systems reliably and easily. In my opinion Docker has with revolutionised cloud-computing. I recommend all system administrators who are still using the traditional approach to seriously consider it. I hope with this article I could demonstrate another way of how much Docker standardise the tasks and make it easier to run complicated systems.