Build reproducible Apptainer (Singularity) container images for RStudio Server with Bioconductor system-library support, deployed as an Open OnDemand (OOD) interactive app across multiple Purdue RCAC HPC clusters.
Two SIF images are produced from a shared codebase:
- Rocky 8 image for Bell and Negishi clusters (kernel 4.18, glibc 2.28)
- Rocky 9 image for Gautschi and Scholar clusters (kernel 5.14, glibc 2.34)
- Proxy required: Build nodes have no direct internet. All dnf, wget, and R
package downloads must go through
http://squid.rcac.purdue.edu:3128. Proxy env vars must be set inside%post, not just%environment, becausesudo apptainer buildsanitizes the environment. - Filesystem: Building SIF directly onto NFS/Lustre (
/scratch,/depot) fails with permission errors. Build to/tmp, thenmvto final location. - Apptainer version: Clusters run Apptainer 1.3.0. Do NOT use
%appsetup(fatal parse error).%apphelpworks but is non-critical. - No Docker daemon: Apptainer pulls Docker base images directly
(
Bootstrap: docker), no Docker build step.
- The container's
%runscriptis not used by OOD in production. The OOD app'sscript.sh.erbcallsapptainer execwith its own argument list. Keep the%runscriptfor manual testing convenience only. - OOD dynamically assigns a port via
${port}shell variable (not$PORT). - Auth is handled by OOD's
template/bin/authscript, not container PAM. The container still needs a PAM config for RStudio's internal checks. - Writable state dirs (
/var/lib/rstudio-server,/var/run/rstudio-server) are bind-mounted from a per-job tmpdir by the OOD script, not baked into the SIF. - External R packages are bind-mounted read-only from a host path to
/opt/R/host-site-libraryinside the container. TheRprofile.sitemust prepend this path to.libPaths().
- R is compiled from source (not from RPM repos) to get full capabilities.
- R configure must include:
--with-readline,--with-libxml2,--enable-R-shlib,--enable-memory-profiling,--with-x=yes,--with-cairo,--with-libpng,--with-jpeglib,--with-libtiff,--enable-java,--with-blas,--with-lapack. - The current production image is MISSING
libxmlandcleditcapabilities. The new build must fix this. - R packages installed at build time must go to the site-library
(
/usr/local/lib64/R/library), NOT to/root/R/.... - RStudio Server RPM: Posit publishes separate RPMs for RHEL 8 and RHEL 9.
URL pattern:
https://download2.rstudio.org/server/rhel{8,9}/x86_64/rstudio-server-rhel-{VERSION}-x86_64.rpm
These are defaults; the build script should accept overrides:
- R: 4.5.2 (Bioconductor 3.21) -- or 4.4.0 (Bioconductor 3.19) if pinned
- RStudio Server: latest stable from Posit (currently 2025.05.0-496)
- Rocky Linux base: 8.10 and 9.5
rstudio-bioc-ood/
CLAUDE.md # This file
ROADMAP.md # Phased execution plan
README.md # User-facing documentation
Makefile # Build orchestration (generates .def, builds .sif)
build.sh # Build script (called by Makefile or standalone)
config/
common.env # Shared config: R version, RStudio version, R packages
rocky8.env # Rocky 8 overrides: base image, repo commands, RPM URL
rocky9.env # Rocky 9 overrides: base image, repo commands, RPM URL
templates/
rstudio-bioc-ood.def.tmpl # Parameterized def template (envsubst-compatible)
generated/ # .gitignore'd; holds generated .def files
ood-app/
bio_rstudio/ # OOD app directory (updated form.yml.erb, script.sh.erb, etc.)
reference/ # Read-only reference material from the current production image
audit/ # Output of apptainer inspect and system queries
deffiles-past/ # Historical def file versions (ver1-ver5)
ood-app/ # Current production OOD app files
docs/
DEPLOY.md # Cluster deployment checklist
TESTING.md # Validation steps for each image
prompts/ # Numbered Claude Code prompts for stepwise execution
| Parameter | Rocky 8 | Rocky 9 |
|---|---|---|
| Base image | docker://rockylinux:8 |
docker://rockylinux:9 |
| Enable extra repos | dnf config-manager --set-enabled powertools |
dnf config-manager --set-enabled crb |
| EPEL install | dnf -y install epel-release |
dnf -y install epel-release |
| RStudio RPM URL tag | rhel8 |
rhel9 |
| OpenJDK default | java-11-openjdk-devel | java-17-openjdk-devel |
| GCC default | 8.5 | 11.x |
| glibc | 2.28 | 2.34 |
| Python default | 3.6 (platform-python) | 3.9 |
| nodejs module syntax | dnf module enable nodejs:20 |
dnf module enable nodejs:20 |
| v8-devel availability | Via nodejs module stream | May need different approach |
| libgit2 | 0.26.x | 1.x (check devel pkg name) |
- R compiled without
--with-readline->cledit = FALSE - R compiled without
--with-libxml2->libxml = FALSE - R packages installed to
/root/R/...instead of site-library apptainer labels $APPTAINER_CONTAINER Squashed ...is fragile for reading labels at build time; use simple shell variables instead
Minimal set for immediate usability. Users install Bioconductor packages to the external host library or their personal library.
BiocManager
tidyverse
devtools
shiny
rmarkdown
httpuv
jsonlite
httr2
DT
plotly
knitr
Rcpp
The following system-level -devel packages must be present so users can
BiocManager::install() packages that need native compilation. This list
was derived from the current production image's RPM inventory (900 packages)
and covers the most common Bioconductor system deps:
Core build tools: make, automake, gcc, gcc-c++, gcc-gfortran, cmake Compression: zlib-devel, bzip2-devel, xz-devel, libzstd-devel Text/regex: pcre2-devel, readline-devel Crypto/network: openssl-devel, libcurl-devel, libssh2-devel, libxml2-devel, libxslt-devel Graphics: cairo-devel, pango-devel, libjpeg-turbo-devel, libtiff-devel, libpng-devel, pngquant Fonts: harfbuzz-devel, freetype-devel, fontconfig-devel, fribidi-devel X11: libXt-devel, libX11-devel, libXext-devel, libXft-devel, libXrender-devel OpenGL: mesa-libGL-devel, mesa-libGLU-devel HDF5/NetCDF: hdf5-devel, netcdf-devel Geospatial: gdal-devel, geos-devel, proj-devel Math/science: gsl-devel, fftw-devel, glpk-devel, gmp-devel, mpfr-devel, openblas, lapack Units: udunits2-devel Database: sqlite-devel, mariadb-devel, libpq-devel (PostgreSQL) Image processing: ImageMagick-devel, ImageMagick-c++-devel Serialization: protobuf-devel, protobuf-compiler Git: git, libgit2-devel Java: java-11-openjdk-devel (Rocky 8) / java-17-openjdk-devel (Rocky 9) MPI: openmpi-devel Misc: zeromq-devel, libarchive-devel, libsodium-devel, valgrind-devel, pam-devel, tcl-devel, tk-devel, expat-devel, ncurses-devel TeX: texlive-collection-latexrecommended (for vignettes/PDF rendering) Python: python3-devel, python3-pandas, python3-pyyaml
The OOD app lives in ood-app/bio_rstudio/ and is deployed to the OOD server
by RCAC Ops. Key files:
form.yml.erb: Defines the web form. Currently hardcoded tonegishi. Needs to be cluster-aware or cloned per cluster.template/script.sh.erb: The main launch script. Already has conditional logic for SIF vs module-based R. Needs a new branch for Rocky 9 SIF path.template/bin/auth: Password auth script. No changes needed.submit.yml.erb: SLURM submission parameters. Cluster-specific.
- Shell scripts: bash,
set -euo pipefail, shellcheck-clean - Def files: generated from templates using
envsubst(no Jinja2 dependency) - All proxy configuration goes through env vars, never hardcoded in dnf.conf
- Comments explain "why", not "what"
- No hardcoded versions; everything comes from config/*.env files