Skip to contents

This vignette shows how to use sysreqr to generate system-requirement snippets for Docker images and CI pipelines. It assumes basic familiarity with both. See vignette("linux-fundamentals") for a primer.

The simple case: one project, one Dockerfile

If you have a project directory and want a Dockerfile snippet that installs its system requirements, pipe check_project() into dockerfile().

plan <- check_project(".", platform = "ubuntu-22.04")
cat(dockerfile(plan))

The output looks like this:

RUN apt-get update && apt-get install -y --no-install-recommends \
    libxml2-dev \
    libcurl4-openssl-dev \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

Paste it into a Dockerfile immediately after the base image, or write it to a side file with write_dockerfile_snippet() and INCLUDE it from your build pipeline.

Always pass platform for Docker builds

When you build a Docker image, the container’s operating system is what matters, not your laptop. If you build a rocker/r-ver image from a Mac, the container is Debian-based. So always supply platform explicitly when generating Dockerfile snippets.

plan <- check_packages("xml2", platform = "ubuntu-22.04")
cat(dockerfile(plan))
#> RUN apt-get update && apt-get install -y --no-install-recommends \
#>     libxml2-dev \
#>     && rm -rf /var/lib/apt/lists/*

The Rocker Project maintains opinionated R images.

Image What you get When to use
rocker/r-ver:<version> Versioned R on Debian Small server-side base
rocker/rstudio:<version> The above plus RStudio Server Interactive development
rocker/tidyverse:<version> tidyverse and friends pre-installed Data-science containers
rocker/geospatial:<version> The above plus GDAL, PROJ, GEOS, etc. Spatial work
rocker/r-base:<version> Plain R from the R Project Debian repository Quick experiments

sysreqr is independent of which base image you pick: it only generates the install commands for the system packages your R packages need.

A two-stage Docker pattern

A common practice is to separate the build image (which has compilers and -dev headers) from the runtime image (which only has runtime libraries). This keeps the deployed image small.

# ---- Stage 1: build ----
FROM rocker/r-ver:4.4 AS build

# System build deps (compilers and headers)
RUN apt-get update && apt-get install -y --no-install-recommends \
    libxml2-dev libcurl4-openssl-dev libssl-dev \
    && rm -rf /var/lib/apt/lists/*

COPY . /src
WORKDIR /src
RUN Rscript install-r-dependencies.R

# ---- Stage 2: runtime ----
FROM rocker/r-ver:4.4

# Runtime libraries only (note: no -dev suffix)
RUN apt-get update && apt-get install -y --no-install-recommends \
    libxml2 libcurl4 libssl3 \
    && rm -rf /var/lib/apt/lists/*

COPY --from=build /usr/local/lib/R/site-library /usr/local/lib/R/site-library
COPY --from=build /src /app
WORKDIR /app
CMD ["R", "--no-save"]

sysreqr::dockerfile() produces the build-stage block. The runtime block, which uses the non--dev variants, is a manual mirror.

Pinning Posit Package Manager snapshots

For reproducible Docker images, point R at a dated PPM snapshot rather than latest.

url <- ppm_repo(platform = "ubuntu-22.04", snapshot = "2026-04-01")
url
#> [1] "https://packagemanager.posit.co/cran/__linux__/jammy/2026-04-01"

Drop those lines into your Dockerfile by way of Rscript -e or an .Rprofile written into the image:

RUN echo 'options(repos = c(CRAN = "https://packagemanager.posit.co/cran/__linux__/jammy/2026-04-01"))' \
    >> /usr/local/lib/R/etc/Rprofile.site

Combined with the system-package install above, this gives bit-for-bit reproducible installs.

GitHub Actions

github_actions() (alias gha()) generates a YAML step that runs the same install commands inside a GitHub-hosted Ubuntu runner.

plan <- check_packages(c("xml2", "curl"), platform = "ubuntu-22.04")
cat(github_actions(plan))
#> - name: Install Linux system dependencies
#>   run: |
#>     sudo apt-get update
#>     sudo apt-get install -y libcurl4-openssl-dev libssl-dev libxml2-dev

Paste it into .github/workflows/<your-workflow>.yaml after the actions/setup-r step:

jobs:
  R-CMD-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: r-lib/actions/setup-r@v2
        with:
          use-public-rspm: true

      - name: Install Linux system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libxml2-dev libcurl4-openssl-dev libssl-dev

      - uses: r-lib/actions/setup-r-dependencies@v2
        with:
          needs: check

If you prefer to let the r-lib/actions ecosystem handle system requirements for you, use the extra-packages and needs inputs of setup-r-dependencies. sysreqr is then most useful for two things:

  1. Pre-CI auditing: run check_project(".") locally before pushing.
  2. CI for non-R-package projects (Shiny apps, scripts, reports), where the standard r-lib actions are less of a fit.

GitLab CI

gitlab_ci() generates a YAML job for GitLab pipelines. GitLab CI jobs usually run as root inside the container image, so the commands are emitted without sudo.

plan <- check_packages(c("xml2", "curl"), platform = "ubuntu-22.04")
cat(gitlab_ci(plan))
#> install_system_requirements:
#>   script:
#>     - apt-get update
#>     - apt-get install -y libcurl4-openssl-dev libssl-dev libxml2-dev

In practice you fold those commands into the before_script of an existing job:

test:
  image: rocker/r-ver:4.4
  before_script:
    - apt-get update
    - apt-get install -y libxml2-dev libcurl4-openssl-dev libssl-dev
  script:
    - Rscript -e 'devtools::check()'

Beyond GitHub Actions and GitLab

The install_command() output is portable across CI systems. For Jenkins, Drone, or shell-driven CI, write the install script once:

write_install_script(plan, file.path(tempdir(), "install-sysreqs.sh"))

Then call sh ci/install-sysreqs.sh from any CI runner.

Binary-first alternatives

On Ubuntu, the r2u apt repository (also available pre-configured in the rocker/r2u Docker image) installs CRAN packages as Ubuntu binaries with system dependencies resolved by apt, which can replace the generated install step entirely for the packages it covers. Similar repositories exist for Fedora (cran2copr) and openSUSE (CRAN2OBS); see vignette("faq") for an overview.

Posit Workbench and Posit Connect

Both Posit Workbench and Posit Connect benefit from Posit Package Manager binary R packages, which avoid source compilation entirely for the distributions they support. use_ppm("user") prints the .Rprofile fragment that points R at the right binary repository; pass dry_run = FALSE and an explicit path to write it.

For Connect specifically, server administrators usually configure the repository at the server level, so end users only need the application code, not .Rprofile edits.

See also