# Make a workspace
cd
mkdir testrv
# The rv cache needs to be outside of the container and bound to it, so we make a directory for it here. I'm putting this into it's own folder in my actual home directory so it can be used across projects.
mkdir ~/.sing_rv_cache
cd testrv
singularity pull oras://ghcr.io/kidcancerlab/r4_6_0:latestWe use --no-home so that R doesn't get confused about your packages, configuration and doesn't improperly write to your home directory
The DISPLAY and XAUTHORITY stuff lets you plot using X11 forwarding from within the container. There are two tricks here. You need to specify where your .Xauthority file is.
For my HPC environment, this is going to either be /tmp/.Xauthority if you are on a login node. When you connect to a compute node, be sure to include --x11 in your srun command:
srun -c 2 --x11 --pty bash
When you do this, your xauthority file is not in the same location. However, its location is specified by the $XAUTHORITY environment variable. You can't use $XAUTHORITY on the login node as this variable is not set. If you set it, your srun request will not work properly due to "magic cookie" errors.
We will define an environment variable "USE_XAUTH" that is defined based on where we find the .Xauthority file
if [[ -n "${XAUTHORITY-}" ]]; then
export USE_XAUTH=${XAUTHORITY}
elif [ -f /tmp/.Xauthority ]; then
export USE_XAUTH=/tmp/.Xauthority
else
echo "No XAUTHORITY file found. Exiting."
exit 1
fisingularity shell \
--no-home \
--cleanenv \
--home "$(pwd):/project" \
--env DISPLAY=$DISPLAY \
--env XAUTHORITY=${USE_XAUTH} \
--bind $HOME/.Xauthority:/tmp/.Xauthority \
--bind $(pwd):/project \
--bind ~/.sing_rv_cache:/cache/rv \
r4_6_0_latest.sifThis is needed on our cluster because $TMPDIR is set when I request a compute node. If $TMPDIR is set, rv will try to use it for caching and fail because it's not writable. Unsetting it allows rv to use the /cache/rv directory that we bind to the container, which is writable.
unset TMPDIRThen, you can initialize rv in the project directory and start adding packages to your rproject.toml file
cd /project
rv initupdate rproject.toml:
Warning
!!!! Make sure you update the bioconductor release version to match your R version !!!!
repositories = [
{ alias = "CRAN", url = "https://cran.rstudio.com/" },
{ alias = "PPM", url = "https://packagemanager.posit.co/cran/latest" },
{ alias = "bioconductor", url = "https://bioconductor.org/packages/3.20/bioc" },
{ alias = "BioCann", url = "https://bioconductor.org/packages/3.20/data/annotation"},
{ alias = "BioCexp", url = "https://bioconductor.org/packages/3.20/data/experiment" },
{ alias = "BioCworkflows", url = "https://bioconductor.org/packages/3.20/workflows"},
]
dependencies = [
"dplyr",
]rv plan
rv syncTo test a container out for development purposes, you can run the container in "sandbox" mode using this command:
singularity build --sandbox sing_sandbox oras://ghcr.io/kidcancerlab/r4_6_0:latest
Then, you can run the container using these commands:
cd
mkdir testrv
mkdir testrv/rv_cache
cd testrv
singularity shell \
--no-home \
--writable \
--fakeroot \
--bind ~/testrv:/project \
--bind ~/testrv/rv_cache:/cache/rv \
sing_sandbox
mkdir sing_sandbox/project
mkdir -p sing_sandbox/cache/rvThis lets you install or change things in the container and test them. Then, copy the relevant commands back to the .def file.
I'm dropping this code here for later reference. It was generated by Gemini and I have not tested it, but I like the concept. It lets you run R code inside the container directly from a Quarto document.
# Define a custom knitr engine
knitr::knit_engines$set(sing_r = function(options) {
# Collapse the chunk code into a single string
code <- paste(options$code, collapse = '\n')
# Wrap it in the apptainer command
# (Change the .sif name to match your actual container)
cmd <- sprintf("apptainer exec my_r_container.sif Rscript -e %s", shQuote(code))
# Execute the command and capture the output
out <- system(cmd, intern = TRUE)
# Return the properly formatted output back to Quarto
knitr::engine_output(options, options$code, out)
})
# do stuff inside the container
print("Hello from inside the container!")