Skip to contents

Motivations

It feels natural and intuitive to use knitrdrawio as part of a Continuous Integration / Continuous Deployment (CI/CD) workflow, for example to automatically your document or book, each time you push a commit.

However, CI/CD typically runs in a headless environment, a server without a graphical display, and/or in Docker containers.

knitrdrawio requires additional dependencies to work in such environments, because of known limitations of the draw.io executable. Being an Electron-based application, draw.io expects and requires a graphical display, and needs to be tricked into thinking there is one available.

Note: if your system is mis-configured, knitrdrawio might believe to be in a headless environment, and complain! In this case, you must make sure that your $DISPLAY environment variable is correctly set, so that knitrdrawio (and, more importantly, drawio itself) may find your display. In particular, if the $DISPLAY variable is empty, there certainly is a problem.

How to

Two methods are available:

  • Using the provided Docker image for a simplified experience, at the cost of a large image size.
  • Manually installing the tools and dependencies to make draw.io work.

Using the provided Docker image

knitrdrawio provides a complete Docker image which already contains all necessary dependencies, such as R, knitr, pandoc, draw.io, etc.

This image is automatically built against the latest release of knitrdrawio, so you can safely use it in your workflows, or reuse it as a base for your own images.

This image can be accessed as ghcr.io/rchaput/knitrdrawio:master, e.g.,

FROM ghcr.io/rchaput/knitrdrawio:master
RUN apt install <your-custom-dependencies>
RUN R -e 'rmarkdown::render("your_document.Rmd")'

Note: this image is inspired from rocker/r-rmd, which includes every dependency for rendering documents, including a TeX distribution (for PDF output). It was meant as a ready-to-use tool, for which you do not have to worry: simply bring your own custom dependencies (if any), and let the image do the job for you. However, this results in a very large image size (several GBs).

Manual installation

You may also manually install the dependencies: knitrdrawio internally has support for some tools and workarounds to make draw.io work when these tools are available.

First, you need to install on the system the following dependencies:

  • xvfb: a virtual display server that behaves as if a “true” graphical display was connected. Read more on the man page.
  • libdrm2, libgbm1, libasound2: libraries that drawio does not include but requires.

These system dependencies can be simply installed through your distribution’s package manager, e.g., apt for Ubuntu and Debian-derivatives systems. Please check with your package manager for the appropriate packages, the name might differ.

Then, you may simply render your document: everything will work, thanks to knitrdrawio’s internal detection and workaround.

To summarize, assuming an Ubuntu (or Debian) based environment:

sudo apt update
sudo apt install libdrm2 libgbm1 libasound2 xvfb
R -e 'rmarkdown::render("your_document.Rmd")'

Performance optimization

However, please note that the previously described steps induce a small overhead: knitrdrawio internally relies on xvfb-run to create a “fake” graphical server each time a diagram should be rendered. This process takes an additional time, and, as the number of diagrams in the document increases, the overhead might become noticeable.

To avoid this and optimize for performance, an additional step can be performed, using the same dependencies. This method creates a virtual server before knitrdrawio is launched, using the same virtual server for all diagrams. Thus, the overhead of starting a virtual server is paid only once.

To do so, before rendering your document, choose a display number, which must be unique on the machine. Usually, 99 should work. Set the $DISPLAY environment variable to :<your number>, e.g., export DISPLAY=:99 (do not forget the : part). Then, start the virtual server with Xvfb &.

You can now render your document: the virtual server, as a background process, will make knitrdrawio (and, ultimately, draw.io) believe there is a graphical server available.

The complete workflow should look like this, assuming an Ubuntu (or Debian) based distribution:

sudo apt update
sudo apt install libdrm2 libgbm1 libasound2 xvfb
export DISPLAY=:99
Xvfb &
R -e 'rmarkdown::render("your_document.Rmd")'

Note that the first two lines (apt update and apt install) are the same as previously, and need to be executed only once. The following two (export DISPLAY and Xvfb &) need to be executed once in a session, i.e., until your close your terminal. Finally, the last line performs the rendering, and can be executed as much as desired. As long as the background process is available, the rendering will work.

Bypassing the headless-detection

By default, knitrdrawio tries to detect whether the current system is headless, by relying on the xrandr tool, or, if it is not available, the $DISPLAY environment variable.

If, for some reason, this detection does not work, or you want to increase performance by avoiding this check every time a chunk is rendered, the detection can be bypassed by setting a global option:

```r
options("knitrdrawio.headless" = FALSE)
```

This option is applied to all subsequent calls to the knitrdrawio engine, and should thus be placed as early as possible. A common place for global options is the .Renv file, which is loaded by R at the beginning of a session. Using a .Renv file makes it easy to have different configurations for a local development system versus a CI/CD headless workflow.

The following values are recognized:

  • NULL (default): perform the headless-detection each time.
  • FALSE: assume the system is not headless, do not perform the detection.
  • TRUE: assume the system is headless, do not perform the detection, and always apply the “headless workaround” through xvfb.

Warning: drawio will fail if the system is headless but no workaround, such as xvfb, is set up. You may also use the FALSE value to disable knitrdrawio’s built-in workaround, if you prefer to manually set up another one.