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 R -e 'install.packages(c("xml2", "curl"))'

# ---- 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.

Beyond GitHub Actions

The install_command() output is portable across CI systems. For GitLab CI:

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()'

For Jenkins, Drone, or shell-driven CI, write the install script once:

write_install_script(plan, "ci/install-sysreqs.sh")

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

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") writes the .Rprofile fragment that points R at the right binary repository.

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