2.2 Align local development and deployment environments

When developing and testing an app locally, it is important to ensure the environment is aligned with the target deployment environment. This might imply using e.g. multiple R and package versions for the local development of different applications, which clash with the typical setup (especially on Linux systems), where only one R version (the latest release) exists.

The idea is then to rely on the same version-stable rocker containers used for the deployments, using a containerized versioned RStudio instance for the local development. This is available through Rocker’s versioned stack, so we could use e.g. rocker/rstudio:3.6.1.

Note that the same version-stable instance of RStudio can be used across all different projects for which such version is relevant. For this reason, a sensible choice is to rely on rocker/verse, which adds tidyverse and devtools to the stack, as well as properly setting up R Markdown system dependencies TinyTeX and pandoc, sparing the effort of the tedious extra install. See the specific section below about ‘TinyTeX considerations’.

2.2.1 Running versioned RStudio instances

Assume we want to run a containerized versioned instance of RStudio for R 3.6.1, possibly alongside instances for other versions of R.

First of all, we need to get the image from docker-hub

docker pull rocker/verse:3.6.1

We then want to have a running instance on localhost (127.0.0.1), with the following setup:

  • No authentication required (local setup).
  • Enable root by setting the environment variable ROOT to TRUE, so that e.g. sudo apt-get can be used in RStudio.
  • Use a version-specific port, e.g. 3500 for R 3.5.0, 3610 for R 3.6.1 and so on, so that we can use localhost for concurrent R version instances.
  • The development code of all relevant projects should live outside the container and be shared with it (and possibly many of them), e.g. under ~/RStudioProjects on the host machine and /home/rstudio/RStudioProjects in the container.
    • For this to work w/o permission issues, the container user (rstudio) must match the UID of the host user ($UID).
  • In order for the RStudio setting to persist if the container is recreated (e.g. after pulling a new rocker image), we also use a shared volume (like ~/.rstudio-docker/3.6.1) for the home/rstudio/.rstudio directory, which is version-specific in case of multiple R versions
  • If we want to use Meld via the compareWith addins, we need to
    • map the DISPLAY environment variable and volume /tmp/.X11-unix
    • add DISPLAY to Renviron
    • install Meld
    • install dbus-x11
  • Use a version-specific name for the container running the RStudio instance, e.g. rstudio_3.6.1.
R_VER=3.6.1
SHARED_DIR=RStudioProjects
docker run -d --restart=always \
  -p 127.0.0.1:$(echo $R_VER | sed 's/[.]//g')0:8787 \
  -e DISABLE_AUTH=true \
  -e ROOT=TRUE \
  -e USERID=$UID \
  -v $HOME/$SHARED_DIR:/home/rstudio/$SHARED_DIR \
  -v $HOME/.rstudio-docker/$R_VER:/home/rstudio/.rstudio \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix:ro \
  --name rstudio_$R_VER \
  rocker/verse:$R_VER
# R and RStudio are not getting the DISPLAY environment variable
docker exec rstudio_$R_VER bash -c \
  'echo "DISPLAY=${DISPLAY}" >> /usr/local/lib/R/etc/Renviron'
# Install Meld
docker exec rstudio_$R_VER bash -c \
  'apt-get update && apt-get install -y --no-install-recommends meld dbus-x11'

The running RStudio can then be accessed by visiting http://localhost:3610/.

You may find convenient to define a shell function as follows

run_rstudio_ver() {
  local R_VER=${1:?"you must supply the R version as first argument"}
  local SHARED_DIR=${2:?"you must supply the shared directory as second argument"}
  local RVER_IMAGE=${3:-"verse"}
  local BASE_IMAGE=rocker/$RVER_IMAGE:$R_VER
  local PORT=$(echo $R_VER | sed 's/[.]//g')0
  local CONTANER_NAME=rstudio_$R_VER
  echo "Containerized version-stable RStudio for R "$R_VER\
       "based on image "$BASE_IMAGE\
       "with shared volume "$SHARED_DIR
  docker pull $BASE_IMAGE &&
  docker run -d --restart=always \
    -p 127.0.0.1:$PORT:8787 \
    -e DISABLE_AUTH=true \
    -e ROOT=TRUE \
    -e USERID=$UID \
    -v $HOME/$SHARED_DIR:/home/rstudio/$SHARED_DIR \
    -v $HOME/.rstudio-docker/$R_VER:/home/rstudio/.rstudio \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix:ro \
    --name $CONTANER_NAME \
    $BASE_IMAGE &&
  # R and RStudio are not getting the DISPLAY environment variable
  docker exec $CONTANER_NAME bash -c \
    'echo "DISPLAY=${DISPLAY}" >> /usr/local/lib/R/etc/Renviron' &&
  # Install Meld
  docker exec $CONTANER_NAME bash -c \
    'apt-get update && apt-get install -y --no-install-recommends meld dbus-x11' &&
  echo "RStudio running in container "$CONTANER_NAME" on port "$PORT &&
  echo "visit http://localhost:"$PORT
}

which you can re-use as compact command for any R version as follows

run_rstudio_ver 3.6.1 RStudioProjects

Note that --restart=always specifies that the container should stay up and restart itself after stopping, e.g. upon machine reboot or docker upgrade, so that it is always available. Still, you can explicitly stop the running container with

docker stop rstudio_3.6.1

Alternatively, you can omit --restart=always and explicitly start the container whenever needed with

docker start rstudio_3.6.1

Note that start/stop operations do not affect the persistence of files created outside the shared location, including global RStudio options such as dark theme, diagnostic, etc. (set via Tools > Global Options…). On the other hand, these files and settings do not (see above) persist removing the container (docker rm, see below) .

2.2.2 TinyTeX considerations

2.2.2.1 pdfcrop

Older rocker/verse images might not include pdfcrop, which is required for the default and desirable cropping of PDF figures with R Markdown (see rocker-org/rocker-versioned#146). Make sure pdfcrop is installed by running at R console

tinytex::tlmgr_install("pdfcrop")

(see R Markdown: The Definitive Guide)

2.2.2.2 Align TinyTeX to current remote repo

NOTE - This should never be needed with recent rocker/verse images, where a version-stable Tex Live repository is used for the TinyTeX install (see rocker-org/rocker-versioned#169).

If you are using LaTeX and start seeing errors like

Remote repository is newer than local (2018 < 2019)

it means that you have to re-install TinyTeX. This happens e.g. with rocker/verse:3.6.1, since it was build at the end of 2018 but the current Tex Live repo is 2019. You can fix this via a user-specific re-installation of TinyTeX for R. NOTE however that this will uninstall the system-level TinyTeX pre-installed in rocker/verse.

First, make sure /home/rstudio/bin is part of the PATH environment variable. Check this by running

docker exec --user rstudio rstudio_3.6.1 R --slave -e 'Sys.getenv("PATH")'

If you don’t see /home/rstudio/bin, you can make sure it is part of the PATH for R via

docker exec --user rstudio rstudio_3.6.1 sh -c 'echo "PATH=$HOME/bin:\${PATH}" >> $HOME/.Renviron'
# check again
docker exec --user rstudio rstudio_3.6.1 R --slave -e 'Sys.getenv("PATH")'

Then, from the running RStudio, run

tinytex::reinstall_tinytex()

2.2.3 Cleanup

docker rm $(docker stop rstudio_3.6.1)