A production-ready container for running Bioconductor with RStudio Desktop on HPC clusters via Apptainer/Singularity.
This repository builds a container image that provides:
- R with full Bioconductor infrastructure and system dependencies
- RStudio Desktop (not Server) for interactive GUI analysis
- HPC-native design: works with SLURM, modules, ThinLinc, Open OnDemand
- Clean package management: layered user/site/container libraries
- Full Bioconductor compatibility: all system libraries from
bioc_fullare included so any Bioconductor package can be compiled by users
The image is designed to be deployed as a read-only Apptainer SIF file, with user-installed R packages stored on the host filesystem.
RStudio Server is a multi-user web application that:
- Runs as a daemon: conflicts with HPC job schedulers
- Binds network ports: security risk on shared compute nodes
- Manages its own authentication: conflicts with HPC auth (LDAP, Kerberos)
- Assumes it controls user sessions: breaks under container isolation
RStudio Desktop is a normal X11 application. It:
- Runs as the user's process: works with SLURM, PBS, LSF
- Requires no ports or daemons: just an X11 display
- Uses the host's authentication: no extra auth layer
- Works with ThinLinc, Open OnDemand, and X11 forwarding
┌──────────────────────────────────────┐
│ Layer 2: HPC Overlay │
│ ┌─────────────┐ ┌────────────────┐ │
│ │ Bioconductor │ │ RStudio Desktop│ │
│ │ BiocManager │ │ Qt/X11 libs │ │
│ │ bioc_full │ │ launch-rstudio │ │
│ │ sys deps │ │ │ │
│ └─────────────┘ └────────────────┘ │
│ R library paths, Renviron, Rprofile │
├──────────────────────────────────────┤
│ Layer 1: rocker/r-ver │
│ R, build tools, CRAN mirror config │
│ Ubuntu, system libraries │
└──────────────────────────────────────┘
Why rocker/r-ver instead of bioconductor_docker?
The official bioconductor/bioconductor_docker inherits from rocker/rstudio,
which bundles RStudio Server. Starting from rocker/r-ver gives us the same R
installation without server infrastructure to strip out. The full set of
Bioconductor system dependencies (from the official bioc_full script) is
installed in the Dockerfile, so any Bioconductor package can be compiled.
# Clone the repository
git clone https://github.com/YOUR_ORG/bioconductor-hpc-container.git
cd bioconductor-hpc-container
# Build Docker image
make build
# Run tests
make test
# Convert to Apptainer SIF
make apptainer# Interactive R session
apptainer exec r-bioconductor_3.23-R-4.6.0.sif R
# Run a script
apptainer exec r-bioconductor_3.23-R-4.6.0.sif Rscript analysis.R
# Launch RStudio Desktop (requires X11)
apptainer exec r-bioconductor_3.23-R-4.6.0.sif rstudio --no-sandboxmodule load bioconductor/3.23
R # Interactive R
Rscript script.R # Run a script
rstudio # Launch RStudio DesktopThe module automatically loads the Singularity/Apptainer module, detects GPUs, clears host compiler variables, and sets safe thread defaults.
All versions are controlled by the VERSION file:
BIOC_VERSION=3.23
R_VERSION=4.6.0
UBUNTU_VERSION=noble
RSTUDIO_VERSION=2026.05.0-218To build with different versions, either edit VERSION or override via
environment:
BIOC_VERSION=3.22 R_VERSION=4.5.1 make buildCheck available upstream versions:
make versions- Docker 20.10+ (or Podman)
- ~10 GB disk space for the build
- Internet access (to download R packages)
# Default build (uses VERSION file)
make build
# Build without cache (full rebuild)
make build-no-cache
# Build with specific versions
BIOC_VERSION=3.22 R_VERSION=4.5.1 make build
# Build directly with docker
docker build \
--build-arg R_VERSION=4.6.0 \
--build-arg R_VERSION_SHORT=4.6 \
--build-arg BIOC_VERSION=3.23 \
--build-arg RSTUDIO_VERSION=2026.05.0-218 \
-t bioconductor-hpc:3.23 .# CLI tests
make test
# RStudio dependency checks
make test-rstudio
# All tests
make test-all
# Manual verification
docker run --rm bioconductor-hpc:3.23 R --version
docker run --rm bioconductor-hpc:3.23 Rscript -e "BiocManager::version()"
docker run --rm bioconductor-hpc:3.23 Rscript -e "library(GenomicRanges)"Requires Docker on the build machine. Fastest method.
# Using the helper script
make apptainer
# Or directly
apptainer build r-bioconductor_3.23-R-4.6.0.sif docker-daemon://bioconductor-hpc:3.23For HPC systems without Docker (e.g., login nodes).
# Using the helper script
make apptainer-def
# Or directly
apptainer build r-bioconductor_3.23-R-4.6.0.sif apptainer/apptainer.defIf the image is pushed to a registry:
apptainer build r-bioconductor_3.23-R-4.6.0.sif docker://registry.example.com/bioconductor-hpc:3.23# CLI tests
./scripts/test_cli.sh --apptainer r-bioconductor_3.23-R-4.6.0.sif
# Manual verification
apptainer exec r-bioconductor_3.23-R-4.6.0.sif R --version
apptainer exec r-bioconductor_3.23-R-4.6.0.sif Rscript -e "library(GenomicRanges)"
apptainer shell r-bioconductor_3.23-R-4.6.0.sif# Copy SIF to shared storage
sudo cp r-bioconductor_3.23-R-4.6.0.sif /apps/biocontainers/images/
sudo chmod 644 /apps/biocontainers/images/r-bioconductor_3.23-R-4.6.0.sifsudo mkdir -p /apps/biocontainers/extras/r-package-site-library/4.6-bioconductor
sudo chmod 2775 /apps/biocontainers/extras/r-package-site-library/4.6-bioconductor
sudo chgrp biocontainer-admins /apps/biocontainers/extras/r-package-site-library/4.6-bioconductor# Auto-detect Lmod vs Tcl
sudo ./scripts/install_module.sh
# Or specify format and path
sudo ./scripts/install_module.sh --lua --module-path /apps/modulefilesmodule load bioconductor/3.23
R --version
Rscript -e "BiocManager::version()"See docs/deployment.md for detailed deployment instructions.
The module provides R, Rscript, and rstudio commands that transparently
invoke Apptainer. It also handles:
- Singularity/Apptainer auto-load: loads the container runtime module
unless
BIOC_SINGULARITY_MODULE=noneis set - GPU detection: automatically passes
--nv(NVIDIA) or--rocm(AMD) to the container runtime when GPUs are present - Compiler isolation: clears host
CC/CXX/FCvariables so R uses its own internal compilers for source package builds - Thread safety: defaults
OMP_NUM_THREADSandOPENBLAS_NUM_THREADSto 1, preventing thread over-subscription in cgroup-limited SLURM jobs - Bind mounts: automatically binds the site library, ThinLinc paths
(
/var/opt,/run/user), and host X11 fonts
# Install
sudo ./scripts/install_module.sh --lua
# Verify
module avail bioconductor
module load bioconductor/3.23
module show bioconductor/3.23# Install
sudo ./scripts/install_module.sh --tcl
# Verify
module avail bioconductor
module load bioconductor/3.23After module load bioconductor/3.23:
| Command | Description |
|---|---|
R |
Interactive R session |
Rscript |
Run R scripts |
rstudio |
Launch RStudio Desktop (needs X11) |
Image location: Set BIOC_IMAGE_DIR to override where the module looks
for the SIF image:
export BIOC_IMAGE_DIR=/my/custom/path
module load bioconductor/3.23Singularity module: By default, the modulefile auto-loads a module named
Singularity. Override this with BIOC_SINGULARITY_MODULE:
# Use a different module name
export BIOC_SINGULARITY_MODULE=apptainer
# Skip auto-loading (apptainer is already in PATH)
export BIOC_SINGULARITY_MODULE=noneThe modulefile uses append_path("APPTAINER_BIND", ...) to add bind mounts.
To add your site's data filesystems, edit the modulefile or set
APPTAINER_BIND before loading:
export APPTAINER_BIND="/data,/project"
module load bioconductor/3.23
# The module appends its own paths (/apps/biocontainers/extras, etc.)R searches for packages in this order:
1. R_LIBS_USER → ~/R/x86_64-pc-linux-gnu-library/4.6 (user, writable)
2. R_LIBS_SITE → /apps/.../4.6-bioconductor (shared, read-only for users)
3. R_HOME/lib → /usr/local/lib/R/site-library (container, read-only)
4. R_HOME/lib → /usr/local/lib/R/library (container, read-only)
User-installed packages override site packages, which override container packages.
# CRAN packages
install.packages("Seurat")
# Bioconductor packages
BiocManager::install("DESeq2")
# Packages install to ~/R/x86_64-pc-linux-gnu-library/4.6All system libraries from the official Bioconductor bioc_full script are
pre-installed in the container, so any package that compiles from source will
find its C/C++/Fortran dependencies.
# Install packages into the shared site library
apptainer exec \
--bind /apps/biocontainers/extras \
/apps/biocontainers/images/r-bioconductor_3.23-R-4.6.0.sif \
Rscript -e "
install.packages('Seurat',
lib = '/apps/biocontainers/extras/r-package-site-library/4.6-bioconductor')
"- The container filesystem is read-only under Apptainer
- Users cannot modify packages inside the container
- All user installs go to
~/R/.../4.6/on the host filesystem - Site installs go to the bind-mounted shared directory
- No writes occur inside the container
ThinLinc provides remote desktop sessions. RStudio Desktop runs as a normal window in the ThinLinc desktop.
# 1. Connect to HPC via ThinLinc client
# 2. Open a terminal
# 3. Load the module and launch RStudio
module load bioconductor/3.23
rstudioFor compute-intensive work, submit to a compute node:
srun --x11 --cpus-per-task=8 --mem=32G --time=4:00:00 \
apptainer exec \
--bind /apps/biocontainers/extras,/scratch \
/apps/biocontainers/images/r-bioconductor_3.23-R-4.6.0.sif \
rstudio --no-sandboxSee docs/thinlinc_example.md for details.
Open OnDemand can launch RStudio Desktop in two ways:
- Interactive Desktop: Launch an OOD desktop, then run RStudio from a terminal (simplest, no OOD app development needed)
- Custom Interactive App: A dedicated OOD app form for RStudio (polished user experience, requires OOD app configuration)
See docs/ood_example.md for complete OOD app
configuration including form.yml, submit.yml.erb, and launch scripts.
srun --cpus-per-task=4 --mem=16G --time=2:00:00 --pty \
apptainer exec \
--bind /apps/biocontainers/extras,/scratch \
/apps/biocontainers/images/r-bioconductor_3.23-R-4.6.0.sif \
R#!/bin/bash
#SBATCH --job-name=bioc-analysis
#SBATCH --cpus-per-task=8
#SBATCH --mem=32G
#SBATCH --time=8:00:00
#SBATCH --output=analysis_%j.log
# Redirect temp files to scratch
export TMPDIR="/scratch/${USER}/tmp/${SLURM_JOB_ID}"
mkdir -p "${TMPDIR}"
# Run analysis
apptainer exec \
--bind /apps/biocontainers/extras \
--bind /scratch \
/apps/biocontainers/images/r-bioconductor_3.23-R-4.6.0.sif \
Rscript analysis.R
# Clean up temp files
rm -rf "${TMPDIR}"library(BiocParallel)
# Use SLURM-allocated cores
ncores <- as.integer(Sys.getenv("SLURM_CPUS_PER_TASK", "1"))
register(MulticoreParam(workers = ncores))
# Your parallel analysis
results <- bplapply(data_list, process_function)Upgrading to a new Bioconductor release requires changing 2-3 variables:
# 1. Check available versions
make versions
# 2. Edit VERSION file
# BIOC_VERSION=3.24
# R_VERSION=4.7.0
# RSTUDIO_VERSION=<latest>
# 3. Rebuild and test
make build
make test-all
# 4. Convert and deploy
make apptainer
sudo cp r-bioconductor_3.24-R-4.7.0.sif /apps/biocontainers/images/
sudo ./scripts/install_module.sh
# 5. Create new site library
sudo mkdir -p /apps/biocontainers/extras/r-package-site-library/4.7-bioconductorSee docs/updating.md for the complete upgrade procedure.
# Check DISPLAY is set
echo $DISPLAY
# Test X11 connectivity
xterm &
# Try with explicit Qt settings
QT_X11_NO_MITSHM=1 QT_QPA_PLATFORM=xcb \
apptainer exec r-bioconductor_3.23-R-4.6.0.sif rstudio --no-sandbox# Verify user library directory exists
ls -la ~/R/x86_64-pc-linux-gnu-library/4.6/
# Create if missing
mkdir -p ~/R/x86_64-pc-linux-gnu-library/4.6
# Verify bind mounts include the site library
apptainer exec --bind /apps/biocontainers/extras ...If R packages fail to compile with compiler errors, host compiler variables may be leaking into the container. The module clears these automatically, but if running without the module:
# Clear host compilers before running
unset CC CXX FC F77 F90 F95
apptainer exec ... Rscript -e "install.packages('...')"# Set TMPDIR to scratch before running
export TMPDIR=/scratch/$USER/tmp
mkdir -p $TMPDIR
apptainer exec ... Rscript analysis.R# Check what's missing
apptainer exec r-bioconductor_3.23-R-4.6.0.sif ldd /usr/lib/rstudio/rstudio | grep "not found"See docs/troubleshooting.md for comprehensive troubleshooting guidance.
- The SIF image is read-only: it cannot be modified at runtime
- Apptainer runs as the invoking user (no root, no setuid by default)
- No network services run inside the container (no listening ports)
- No daemons: everything runs as foreground processes
- UID/GID from the host are passed through: file permissions work normally
- Sensitive data (home directories, project files) is accessed via bind mounts with the user's own permissions
- TMPDIR: Always redirect to scratch storage for data-intensive workflows.
The default
/tmpis often a small tmpfs that will fill during scRNA-seq or genome-scale analyses. - Memory: R holds data in memory. Request sufficient SLURM memory for your dataset. Common sizes: 16 GB for small analyses, 32-64 GB for scRNA-seq, 128+ GB for large genome assemblies.
- Threads: The module defaults
OMP_NUM_THREADSandOPENBLAS_NUM_THREADSto 1 to prevent over-subscription. Override in your SLURM script when you have multiple cores allocated. - Parallelism: Use
BiocParallel::MulticoreParam()withSLURM_CPUS_PER_TASKto match the allocated core count. - I/O: Avoid reading/writing large files to NFS home directories in tight
loops. Use scratch or local SSD (
/tmpon some clusters) for intermediate files, then copy final results to permanent storage.
This repository is designed for minimal-touch maintenance across Bioconductor release cycles:
- Version changes are centralized in the
VERSIONfile - Upstream inheritance means we get R and Ubuntu updates from rocker
- Bioconductor is installed via BiocManager, not baked in: the version is controlled by a single variable
- Full system deps from bioc_full: users can compile any Bioconductor package
- No forking of upstream images: we layer on top, not replace
- Scripts are parameterized: they read from
VERSION, not hardcoded values
The expected maintenance cadence is:
- Every 6 months: Update for new Bioconductor release (change 2-3 variables)
- As needed: Update RStudio Desktop version
- Rarely: Modify system dependencies (only when Bioconductor adds new package types that need system libraries)
.
├── README.md # This file
├── LICENSE # MIT License
├── .gitignore # Git ignore patterns
├── Dockerfile # Docker build definition
├── Makefile # Build automation
├── VERSION # Version configuration
├── scripts/
│ ├── build.sh # Docker build script
│ ├── build_apptainer.sh # Apptainer conversion
│ ├── test_cli.sh # CLI test suite
│ ├── test_rstudio.sh # RStudio dependency checks
│ ├── launch_rstudio.sh # RStudio launcher (inside container)
│ ├── install_module.sh # Module deployment
│ └── detect_versions.sh # Upstream version checker
├── modulefiles/
│ └── bioconductor # Example Lmod modulefile
├── apptainer/
│ ├── apptainer.def # Apptainer definition file
│ └── environment.sh # Host environment setup
├── env/
│ ├── renviron.site # R environment variables
│ ├── rprofile.site # R startup code
│ └── profile.d/
│ └── bioc.sh # Shell environment
├── docs/
│ ├── architecture.md # Architecture rationale
│ ├── deployment.md # HPC deployment guide
│ ├── updating.md # Upgrade procedures
│ ├── ood_example.md # Open OnDemand integration
│ ├── thinlinc_example.md # ThinLinc integration
│ └── troubleshooting.md # Problem resolution
├── templates/
│ ├── modulefile.tcl # Tcl modulefile template
│ └── modulefile.lua # Lmod modulefile template
└── .github/
└── workflows/
└── build.yml # CI/CD pipeline
MIT License. See LICENSE.
