Skip to content

Serve bettr

We are currently serving bettr via shiny-server within singularity. So this recipe aims to install singularity on a linux machine, get the bettr image, and set up a cron to retrieve metrics.

The host needs to face the internet. And a firewall.

We are aware rstudioconnect could simplify this, or just running bettr locally.



We assume an apt-apt distribution, i.e. debian or ubuntu.

sudo apt-get update

sudo apt-get update && sudo apt-get install -y \
    build-essential \
    uuid-dev \
    libgpgme-dev \
    squashfs-tools \
    libseccomp-dev \
    wget \
    pkg-config \
    git \
    cryptsetup-bin \

mkdir -p ~/soft/go
cd $_

export VERSION=1.13.5 OS=linux ARCH=amd64 && \

sudo tar -C /usr/local -xzvf go$VERSION.$OS-$ARCH.tar.gz

echo 'export GOPATH=${HOME}/go' >> ~/.bashrc && \
    echo 'export PATH=/usr/local/go/bin:${PATH}:${GOPATH}/bin' >> ~/.bashrc && \
    source ~/.bashrc

mkdir -p ~/soft/singularity
cd $_

export VERSION=3.8.3 && # adjust this as necessary \

tar -xzf singularity-*${VERSION}.tar.gz

cd sing*

./mconfig && \
    make -C ./builddir

sudo make -C ./builddir install

# test

singularity exec library://alpine cat /etc/alpine-release


sudo apt install apache2

Also, configure iptables. Or ufw, open port 80/443, 22, and whatever the port the bettr image is going to use below.

bettr image

The bettr image is generated by .

serving the bettr app

To read the bettr image from the registry, an user token with read_registry power is needed; below encoded as INGULARITY_DOCKER_PASSWORD.

mkdir -p ~/bettr_deployer/apps ~/bettr_deployer/logs ~/bettr_deployer/lib ~/bettr_deployer/tmp

cd ~/bettr_deployer
export SINGULARITY_DOCKER_PASSWORD='KxasgasgasgxK' # read_registry granted
export NAMESPACE="omnibenchmark"
export ID="obm_bettr"
export VERSION="1d0b31b"

singularity instance start --env SHINY_LOG_STDERR=1 \
            --bind ./apps:/srv/shiny-server/bettr \
            --bind ./log:/var/log/shiny-server_bettr \
            --bind ./lib:/var/lib/shiny-server \
            --bind ./tmp:/tmp \
            bettr-deployer_"$ID"-"$VERSION".sif "$ID"_"$VERSION"

singularity exec instance://"$ID"_"$VERSION" "shiny-server" &

So apps will be served if placed at ~/bettr_deployer/apps, i.e.:

total 12
-rw-rw-r-- 1 shiny shiny 1614 Jul 27 13:28 app.R
drwxrwxr-x 2 shiny shiny 4096 Sep  1  2022 data
-rw-rw-r-- 1 shiny shiny    0 Sep  1  2022 restart.txt

total 340
-rw-rw-r-- 1 shiny shiny 347951 Jan 19  2023 summary.json

where app.R contains

#!/usr/bin/env R

## Retrieve data

## make recognize the argument as the data that has been just downloaded, for FAIR

resDir   <- 'data'
bstheme  <- 'darkly'
appTitle <- 'bettr'
metrics  <- 'all'

res_files <- list.files(path = paste0(resDir), full.names = TRUE)

## Read result files
out <- jsonlite::read_json(res_files, simplifyVector = TRUE)

## reconverting true false to logical
colnames(out$metricInfo) <- c("Metric", "Group")
out$initialTransforms <- lapply(out$initialTransforms, function(x){
  lapply(x, function(y){

# replacing na value
out$idInfo[$idInfo)] <- "NaN"

# call bettr
  df = out$df,
  idCol = if (is.null(out$idCol)) {
  } else {
  metrics = if (is.null(out$metrics)){
    setdiff(colnames(out$df), out$idCol)
  } else {
  initialTransforms = if (is.null(out$initialTransforms)) {
  } else {
  metricInfo = out$metricInfo,
  metricColors = out$metricColors,
  idInfo = out$idInfo,
  idColors = out$idColors,
  weightResolution = if (is.null(out$weightResolution)) {
  } else {
  bstheme = if (is.null(out$bstheme)) {
  } else {
  appTitle = if (is.null(out$appTitle)) {
  } else {

And the data is either a fixed snapshot (or retrieved periodically via cron) of a suitably formated metrics file, like this one.

firewall / shiny ports

Mind to open port 3840 to fit the docker container shiny-server's port, or update the Dockerfile or the singularity port mapping as needed.

cron to retrieve results

Using the clustering benchmark as an example:

cd /home/shiny/bettr_deployer/apps/omni_clustering/data

wget -q -O summary.json

## to make sure the shiny app reloads
touch ../app.R

Tips and troubleshooting