| name | singularity-recipe |
| description | Use when writing, modifying, or reviewing a Singularity `.def` or Apptainer recipe file, especially for bioinformatics tool distribution. Also use when asked about container layering strategy, base image choice, `%post`/`%environment`/`%runscript`/`%test` sections, version pinning inside containers, or multi-stage builds. |
Singularity recipes for bioinformatics tools
Section responsibilities
A recipe has five sections that each do one thing. Mixing them causes subtle
bugs; keep them separate.
| Section | Purpose |
|---|
%post | Install software at build time |
%files | Copy files from host into image at build time |
%environment | Export env vars every time the container runs |
%runscript | Default command when user runs singularity run <img> |
%test | Assertions that must pass for build to succeed |
%labels | Metadata readable via singularity inspect |
%help | Shown by singularity run-help <img> |
Base image choice
- Conda-based tool:
Bootstrap: docker, From: condaforge/mambaforge:latest
— mamba is faster than conda.
- Compiled C/C++ tool:
From: ubuntu:22.04, install build-essential in
%post, clean up at the end.
- R-heavy tool:
From: rocker/r-ver:4.4.0 for pinned R, or build R via
conda if mixing with Python tools.
- Minimum size matters:
From: alpine:3.19 works for pure-static binaries
but fights glibc-linked tools. Default to ubuntu.
The canonical %post for a mixed Python+R bioinformatics tool
%post
set -euxo pipefail
apt-get update
apt-get install -y --no-install-recommends \
build-essential git curl ca-certificates
mamba create -y -n tool -c conda-forge -c bioconda \
python=3.11 \
samtools=1.19 \
bcftools=1.19 \
pysam=0.22.0 \
r-base=4.4.0 \
bioconductor-genomicranges=1.54.1
cd /opt
git clone --depth 1 --branch v1.2.3 \
https://github.com/kavonrtep/toolname.git
cd toolname
conda run -n tool pip install --no-deps .
conda clean -afy
apt-get autoremove -y build-essential
apt-get clean
rm -rf /var/lib/apt/lists/*
Notes:
set -euxo pipefail — fail loudly. The default shell eats errors.
--no-deps on pip install — deps are already in the conda env, pinned.
- Pin your own package with a tag or commit SHA, never
master.
- Clean conda and apt caches — easy 1–2 GB saving.
%environment — what belongs here
Only things every invocation needs:
%environment
export PATH=/opt/conda/envs/tool/bin:$PATH
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
export TOOL_CONFIG_DIR=/opt/toolname/config
Do not put per-run variables here (sample names, paths). Those are
singularity exec --env or CLI args.
%test — a real assertion, not echo ok
%test
set -e
samtools --version | grep "samtools 1.19"
python -c "import pysam; assert pysam.__version__.startswith('0.22')"
toolname --version
toolname --help > /dev/null
toolname run /opt/toolname/tests/data/mini.fa -o /tmp/out.gff
test -s /tmp/out.gff
If %test fails, singularity build exits non-zero. That's the point.
%runscript — single entry point
%runscript
exec toolname "$@"
That's it. Complex dispatch belongs in the tool's CLI, not the recipe.
%labels — what to include
Maintainer Petr Novak <petr@umbr.cas.cz>
Version 1.2.3
Source https://github.com/kavonrtep/toolname
BuildDate {{BUILD_DATE}}
Build commands for this project
sudo singularity build --force {{IMAGE}}.sif {{RECIPE}}.def
singularity test {{IMAGE}}.sif
singularity run {{IMAGE}}.sif --help
Anti-patterns seen in real recipes
apt-get install of a tool that's available on bioconda — use bioconda
for reproducibility.
- Installing
pip install -r requirements.txt with unpinned requirements.
- No
%test — build passes, runtime fails.
- Writing
/root/... paths that break under user-namespace Singularity.
From: bioconda/bioconda-utils-build-env as a runtime base — it's a
build-time image, huge and wrong.
- Multi-GB
.sif from not cleaning package caches.
Delivering the container
Petr's projects typically ship .sif via:
- GitHub release asset (for small images, <500 MB)
- Zenodo archive (for DOI citation in a paper)
oras:// push to a registry (for pipelines that pull at runtime)
Which one applies is in .claude/project.yml under deploy.singularity_registry.