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/*Recommended base images
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.siteCombined 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-devPaste 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: checkIf 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:
-
Pre-CI auditing: run
check_project(".")locally before pushing. - 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-devIn practice you fold those commands into the
before_script of an existing job:
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
-
vignette("preflight-setup")for the basic workflow. -
vignette("linux-fundamentals")for the system-level concepts. -
vignette("faq")for common gotchas.
