Environment System#
The environment system manages software environment resolution, activation, and deactivation for each rule in a workflow.
Overview#
Each rule can declare an environment specification. Before a rule's shell command runs, oxo-flow:
- Resolves the environment spec to a concrete backend
- Ensures the environment is ready (created, pulled, etc.)
- Activates the environment
- Runs the shell command
- Deactivates the environment
Architecture#
graph TD
Rule["Rule (environment spec)"] --> Resolver["EnvironmentResolver"]
Resolver --> Conda["CondaBackend"]
Resolver --> Pixi["PixiBackend"]
Resolver --> Docker["DockerBackend"]
Resolver --> Singularity["SingularityBackend"]
Resolver --> Venv["VenvBackend"]
Resolver --> Cache["EnvironmentCache"]
Cache --> File["Cache File (JSON)"]
EnvironmentResolver#
The central coordinator that:
- Detects available backends on the system
- Validates environment specifications
- Dispatches to the appropriate backend
- Tracks environment setup state via EnvironmentCache
let resolver = EnvironmentResolver::new();
let available = resolver.available_backends(); // ["conda", "docker", "venv"]
resolver.validate_spec(&rule.environment)?;
EnvironmentCache#
Tracks which environments have been successfully set up:
- In-memory cache: Tracks ready environments during execution
- Persistent cache: Optionally saves state to a JSON file for reuse across runs
// Create resolver with persistent cache
let resolver = EnvironmentResolver::with_cache_dir(Path::new(".oxo-flow/cache"));
When using --cache-dir, oxo-flow saves environment setup state after each run. Subsequent runs skip setup for already-ready environments, reducing startup time.
Environment Setup Process#
Before executing a rule with an environment specification:
- Check cache: If the environment is already marked as ready, skip setup
- Run setup command: Execute the backend's setup command (e.g.,
conda env create -f env.yaml) - Mark ready: Cache the environment as successfully set up
Setup Commands by Backend#
| Backend | Setup Command |
|---|---|
| Conda | conda env create -f <yaml_file> |
| Pixi | pixi install (if pixi.toml exists) |
| Docker | docker pull <image> |
| Singularity | singularity pull <image> |
| Venv | python -m venv <path> && pip install -r <requirements> |
Skipping Setup#
Use --skip-env-setup when environments are pre-built:
Backend Implementations#
Conda#
- Detection: Checks for
condaon$PATH - Resolution: Parses YAML environment file
- Activation: Runs
conda activate <env_name>before the command - Caching: Environments are created once and reused across rules that share the same YAML file
Pixi#
- Detection: Checks for
pixion$PATH - Resolution: Parses
pixi.tomlproject file - Activation: Uses
pixi runto execute within the environment - Lockfile: Pixi's native lockfile ensures reproducible resolution
Docker#
- Detection: Checks for
dockeron$PATHand daemon availability - Resolution: Parses image reference (registry/image:tag)
- Execution: Wraps shell command in
docker run --rm -v $(pwd):$(pwd) -w $(pwd) <image> <cmd> - Pull policy: Images are pulled on first use if not locally available
Singularity / Apptainer#
- Detection: Checks for
singularityorapptaineron$PATH - Resolution: Parses image reference (can be
docker://,.siffile, or library URI) - Execution: Wraps shell command in
singularity exec <image> <cmd> - Binding: Working directory is automatically bound into the container
Python venv#
- Detection: Checks for
python3andpipon$PATH - Resolution: Parses
requirements.txtfile - Activation: Creates a venv (if needed) and activates it before the command
- Caching: Venvs are stored in a cache directory keyed by the requirements hash
Environment Specification#
The EnvironmentSpec struct supports one backend per rule:
pub struct EnvironmentSpec {
pub conda: Option<String>,
pub pixi: Option<String>,
pub docker: Option<String>,
pub singularity: Option<String>,
pub venv: Option<String>,
pub modules: Vec<String>,
}
In TOML:
# Only one backend per rule (plus optional HPC modules)
environment = { conda = "envs/tools.yaml" }
environment = { docker = "biocontainers/bwa:0.7.17" }
environment = { venv = "envs/requirements.txt" }
environment = { modules = ["gcc/11.2", "cuda/11.7"] }
If multiple backends are specified, the first one found is used in this priority order: docker, singularity, conda, pixi, venv. HPC modules are loaded regardless of the primary backend if the executor supports them.
Default Environments#
Set a default in [defaults]:
Rules without an explicit environment field inherit the default. Rules with an explicit environment override the default completely.
Validation#
# Check that all backends are available
oxo-flow env list
# Validate all environments in a workflow
oxo-flow env check pipeline.oxoflow
The env check command verifies:
- The backend type is available on the system
- The specification file exists (for conda YAML, pixi TOML, requirements.txt)
- The image reference is syntactically valid (for Docker/Singularity)
See Also#
- Environment Management tutorial — getting started
- Use Environments how-to — practical recipes
envcommand — CLI referenceruncommand —--skip-env-setupand--cache-diroptions