Containers (Docker & Singularity)¶
Prefer not to install pixi at all? Each environment ships as its own
container image, so you pull only the one you need. This is handy on shared
servers and HPC clusters, or anywhere you want a guaranteed-identical setup.
It's especially useful on older Linux systems (e.g. CentOS 7), where a
direct pixi install can be hard or impossible because the host is too old:
the container carries its own modern userspace, so it only needs a compatible
kernel — not an up-to-date OS.
Images are published to GHCR, one tag per environment:
| Image | Contains |
|---|---|
ghcr.io/liuhlab/liulab-runtime:align-rna |
RNA-seq alignment (STAR + QC) |
ghcr.io/liuhlab/liulab-runtime:align-dna |
DNA-seq alignment (chromap + QC) |
ghcr.io/liuhlab/liulab-runtime:ml |
PyTorch (CPU/MPS) + scvi-tools + scanpy |
ghcr.io/liuhlab/liulab-runtime:ml-gpu |
The ML stack built against an NVIDIA CUDA GPU |
Each image holds a single environment (baked in — no need to pick one at
runtime). A moving :<env> tag tracks the latest build; released versions are
also pinned as :<env>-<version> (e.g. :align-rna-2026.7.3). Which envs are
published is the docker-environments list in pyproject.toml.
amd64 only
The runtime defines a linux-64 platform but no linux-aarch64 one, so
the images are amd64-only. On an Apple Silicon Mac they run emulated —
add --platform=linux/amd64 to your docker commands.
Docker¶
Pull & run¶
# Pull the env you need
docker pull ghcr.io/liuhlab/liulab-runtime:align-rna
# Run a command in it (the env is baked in — no -e needed)
docker run --rm ghcr.io/liuhlab/liulab-runtime:align-rna STAR --version
# Interactive shell
docker run --rm -it ghcr.io/liuhlab/liulab-runtime:ml
# Work on your own data (mount a host folder)
docker run --rm -it -v "$PWD/data:/data" ghcr.io/liuhlab/liulab-runtime:align-dna
# Jupyter Lab — then open http://localhost:8888
docker run --rm -it -p 8888:8888 -v "$PWD:/data" ghcr.io/liuhlab/liulab-runtime:ml \
pixi run lab --ip=0.0.0.0 --no-browser --allow-root
GPU (ml-gpu)¶
The ml-gpu image needs the host NVIDIA driver injected at runtime — the
conda pytorch-gpu build bundles the CUDA runtime libraries itself, so no CUDA
base image is required, only a compatible driver (CUDA ≥ 12.0; the build runs
on 12.6 nodes thanks to CUDA 12 minor-version compatibility).
docker run --rm --gpus all ghcr.io/liuhlab/liulab-runtime:ml-gpu \
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
Build locally¶
git clone https://github.com/liuhlab/liulab-runtime.git
cd liulab-runtime
# Pick the env with --build-arg PIXI_ENV
docker build --build-arg PIXI_ENV=align-rna -t liulab-runtime:align-rna .
# The ml-gpu image builds on any amd64 machine — no GPU needed at build time
docker build --build-arg PIXI_ENV=ml-gpu -t liulab-runtime:ml-gpu .
Building ml-gpu without a GPU
A GPU is only needed to run ml-gpu, not to build it — the build just
downloads the CUDA packages. The Dockerfile sets CONDA_OVERRIDE_CUDA at
build time so the CUDA env resolves on a GPU-less builder (CI, a laptop). On
an Intel Mac this builds at native speed (it's already linux/amd64); on
Apple Silicon it works but is emulated and slow. You can't test the GPU on a
Mac — validate that on an NVIDIA node with --nv / --gpus all.
Singularity / Apptainer¶
Most clusters use Singularity (Apptainer) instead of Docker. Pull the same public per-env image straight from GHCR — no Docker, no root needed.
On the lab's IRCBC cluster?
This page stays general (any machine or cluster). Lab members working on IRCBC should follow the IRCBC HPC in-house tutorial instead — it covers the exact cluster workflow end to end: SSH setup, the shared image, and JupyterLab over a tunnel.
Pull¶
# `pull` converts the image into a single .sif, named after the tag.
singularity pull docker://ghcr.io/liuhlab/liulab-runtime:align-rna
# -> liulab-runtime_align-rna.sif
(Or build from the bundled definition file, choosing the env with a build arg:
singularity build --build-arg ENV=align-rna liulab-runtime_align-rna.sif liulab-runtime.def.)
Use¶
# Run a command in the image's environment (via `singularity run`, which
# activates the baked env — don't prefix with `pixi run`)
singularity run liulab-runtime_align-dna.sif chromap --version
singularity run liulab-runtime_ml.sif python -c "import scanpy"
# Interactive shell
singularity shell liulab-runtime_align-rna.sif
# inside: pixi shell -e "$LIULAB_ENV"
# The env is also active under `exec` (which bypasses the runscript), so the
# tools are on PATH directly — no `pixi run` prefix, no PATH setup:
singularity exec liulab-runtime_align-rna.sif STAR --version
# GPU image — add --nv so the host driver is visible
singularity run --nv liulab-runtime_ml-gpu.sif \
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
# Jupyter Lab on a compute node
singularity run liulab-runtime_ml.sif lab --ip=0.0.0.0 --no-browser
Workflow engines (Snakemake, Nextflow)
Because the env is active under exec, these images work as drop-in tool
containers. A Snakemake rule with container:
"docker://ghcr.io/liuhlab/liulab-runtime:align-rna" run with
--software-deployment-method apptainer finds STAR out of the box — no
APPTAINERENV_PREPEND_PATH or hand-added .pixi/envs/*/bin.
Read-only image
A .sif is read-only, and each image carries just one environment. Run
analyses against the baked-in env and keep your data on bind mounts —
don't try to pixi install/pixi update inside a running container. Need a
different environment? Pull its image tag.