Skip to contents

This vignette describes how to run NEBULA inside a container. Using a container is recommended when you want to guarantee a fully reproducible environment, when system dependencies (C++ toolchain, PLINK, Bioconductor packages) are difficult to install directly, or when running analyses on a high-performance computing (HPC) cluster.

Two container runtimes are supported:

  • Docker – suitable for local workstations and cloud environments.
  • Singularity / Apptainer – the standard runtime on most HPC clusters; does not require root privileges at run-time.

Docker

Prerequisites

Install Docker Desktop (Linux, macOS, Windows).

On Linux you can run Docker without sudo by adding your user to the docker group. Follow the official post-installation steps if needed.

Build the image

A Dockerfile is provided in the docker/ directory of the repository. From the repository root, run:

docker build -f docker/Dockerfile -t nebula .

This builds an image named nebula that contains R, PLINK, and NEBULA with all its dependencies.

Run a script inside the container

Mount the directory that contains your data and output folder and pass your R script to Rscript:

docker run \
  -v $(pwd)/data:/data \
  -v $(pwd)/output:/output \
  -ti nebula \
  Rscript /data/your_script.R
Flag Purpose
-v $(pwd)/data:/data maps the local data/ folder into the container at /data
-v $(pwd)/output:/output maps the local output/ folder into the container at /output
-ti allocates a pseudo-terminal (useful for interactive sessions)

Inside the R script, refer to input files using the container paths /data/... and write results to /output/....

Interactive session

To open an R session directly inside the container:

docker run \
  -v $(pwd)/data:/data \
  -v $(pwd)/output:/output \
  -ti nebula R

Singularity / Apptainer

Singularity (now continued as Apptainer) is the preferred runtime on HPC systems because it does not require a running daemon and does not grant root access to the host.

Use the pre-built image

A pre-built Singularity image nebula.sif is provided in the repository root. Copy it to your HPC project directory and use it directly:

singularity exec \
  --bind $(pwd)/data:/data \
  --bind $(pwd)/output:/output \
  nebula.sif \
  Rscript /data/your_script.R

Build the image from the Dockerfile

If you want to build the Singularity image yourself (requires root or a system with --fakeroot support):

# Convert the Docker image to a Singularity image
docker build -f docker/Dockerfile -t nebula .
singularity build nebula.sif docker-daemon://nebula:latest

Alternatively, build directly from the Docker Hub / GitLab registry if the image is published there.

Submit as an HPC job (SLURM example)

#!/bin/bash
#SBATCH --job-name=nebula
#SBATCH --cpus-per-task=6
#SBATCH --mem=16G
#SBATCH --time=04:00:00

singularity exec \
  --bind $SLURM_SUBMIT_DIR/data:/data \
  --bind $SLURM_SUBMIT_DIR/output:/output \
  nebula.sif \
  Rscript /data/your_script.R

Adjust --cpus-per-task to match the N_cores argument used inside the R script.

Writing portable R scripts for container use

When writing R scripts that will run inside the container, use the mounted paths consistently:

library(nebula)

root_file_name  <- "/data/genotype"           # PLINK prefix (no extension)
pathpathways    <- "/data/snpsets"            # SNP-set directory
pathwaylistfile <- "/data/snpset_list.txt"    # SNP-set list file
res_folder      <- "/output"

dir.create(res_folder, showWarnings = FALSE, recursive = TRUE)

nebula::compute_null_hypothesis(
  root_file_name  = root_file_name,
  pathpathways    = pathpathways,
  pathwaylistfile = pathwaylistfile,
  nullhypmsfile   = file.path(res_folder, "null_ms.txt"),
  nullhyps2file   = file.path(res_folder, "null_s2.txt"),
  seed = 1, B = -1, alpha = 0.05,
  min_snps = 2, max_snps = 999999,
  N_cores = 6, n_rows = 10,
  verbosity = 0, mode = 0, implementation = 0
)

results <- nebula::compute_association(
  root_file_name  = root_file_name,
  pathpathways    = pathpathways,
  pathwaylistfile = pathwaylistfile,
  nullhypmsfile   = file.path(res_folder, "null_ms.txt"),
  nullhyps2file   = file.path(res_folder, "null_s2.txt"),
  alpha = 0.05, B = -1,
  out_selected = file.path(res_folder, "association_results.txt"),
  min_snps = 2, max_snps = 999999,
  N_cores = 6, n_rows = 5000,
  verbosity = 0, mode = 0, association_mode = "MAX"
)

corrected_results <- nebula::correct_association(
  results,
  GRCh = "38",
  output_path = res_folder
)