---
title: Recipe Format
description: Complete specification of the sparkrun recipe YAML format.
---

A sparkrun recipe is a YAML file that describes everything needed to launch an inference workload: the model, the container image, the runtime engine, default parameters, and the serve command. Recipes are the central abstraction in sparkrun — they let you capture a known-good configuration once and replay it with a single command.

```bash
sparkrun run my-recipe --solo          # use defaults
sparkrun run my-recipe -H host1,host2  # override hosts
sparkrun run my-recipe -o port=9000    # override any default
```

## Minimal recipe

The smallest useful recipe needs only `model`, `runtime`, `container`, and `command`:

```yaml
model: Qwen/Qwen3-1.7B
runtime: vllm
container: scitrera/dgx-spark-vllm:0.16.0-t5

defaults:
  port: 8000
  host: 0.0.0.0

command: |
  vllm serve {model} --host {host} --port {port}
```

## Full recipe example

```yaml
model: nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4
model_revision: abc123def
runtime: vllm
min_nodes: 1
container: scitrera/dgx-spark-vllm:0.16.0-t5

metadata:
  description: NVIDIA Nemotron 3 Nano 30B (upstream NVFP4) -- cluster or solo
  maintainer: scitrera.ai <open-source-team@scitrera.com>
  model_params: 30B
  model_dtype: nvfp4

defaults:
  port: 8000
  host: 0.0.0.0
  tensor_parallel: 1
  gpu_memory_utilization: 0.8
  max_model_len: 200000
  served_model_name: nemotron3-30b-a3b
  tool_call_parser: qwen3_coder

env:
  VLLM_ALLOW_LONG_MAX_MODEL_LEN: "1"

command: |
  vllm serve \
      {model} \
      --served-model-name {served_model_name} \
      --max-model-len {max_model_len} \
      --gpu-memory-utilization {gpu_memory_utilization} \
      -tp {tensor_parallel} \
      --host {host} \
      --port {port} \
      --enable-auto-tool-choice \
      --tool-call-parser {tool_call_parser} \
      --trust-remote-code
```

## Core fields

### `model` (required)

The HuggingFace model identifier to serve.

```yaml
# Standard HuggingFace model
model: Qwen/Qwen3-1.7B

# GGUF model with quantization variant (llama-cpp runtime)
model: Qwen/Qwen3-1.7B-GGUF:Q8_0
```

For GGUF models, the colon syntax (`repo:quant`) selects a specific quantization variant. sparkrun uses this to download only the matching quant files rather than the entire repository.

The model value is injected into the command template as `{model}` and is also used for model pre-sync (downloading and distributing weights to target hosts before launch).

### `model_revision`

Pin the model to a specific HuggingFace revision (branch, tag, or commit hash).

```yaml
model: QuantTrio/MiniMax-M2.5-AWQ
model_revision: bbe738792c
```

This affects model download, cache checking, VRAM estimation, and model sync. Use it for reproducible deployments — without it, a model's `main` branch may change between downloads.

### `runtime`

Which inference engine to use:

| Value | Engine | Clustering | Notes |
|---|---|---|---|
| `vllm` | vLLM | varies | Virtual alias — resolves to `vllm-distributed` (default) or `vllm-ray`. See [vLLM runtime](/runtimes/vllm/). |
| `vllm-distributed` | vLLM | Native | Default vLLM variant. Uses vLLM's built-in distributed backend. |
| `vllm-ray` | vLLM | Ray | vLLM with Ray head/worker orchestration. |
| `sglang` | SGLang | Native | First-class. Solo and multi-node. |
| `llama-cpp` | llama.cpp | N/A | Solo mode. GGUF models. |
| `eugr-vllm` | vLLM (eugr) | Ray | Extends `vllm-ray` with eugr builds and mods. |

Explicitly setting `runtime` is **recommended** for clarity and forward-compatibility. However, this field is technically optional — when omitted, sparkrun uses [automatic runtime detection](#automatic-runtime-detection) to infer the runtime from the `command` field. If neither `runtime` nor a recognizable command is present, the recipe defaults to `vllm` behavior.

### `container` (required)

The Docker/OCI container image to run.

```yaml
container: scitrera/dgx-spark-vllm:0.16.0-t5
container: scitrera/dgx-spark-sglang:0.5.8-t5
container: scitrera/dgx-spark-llama-cpp:b8076-cu131
```

### `command` (recommended)

The shell command to execute inside the container. Uses `{placeholder}` syntax — any key from `defaults` (or CLI overrides) can be referenced.

```yaml
command: |
  vllm serve \
      {model} \
      --served-model-name {served_model_name} \
      --host {host} \
      --port {port}
```

Substitution is iterative: if a placeholder resolves to a string containing another `{placeholder}`, it will be expanded in a second pass.

## Topology fields

:::tip[Prefer min_nodes / max_nodes]
Use `min_nodes` and `max_nodes` to set appropriate limits for your recipe. The `solo_only`, `cluster_only`, and `mode` fields exist for convenience and backward compatibility, but explicit node limits are clearer and more flexible.
:::

### `mode`

Explicit topology mode. Usually not needed — sparkrun infers from `min_nodes` and `max_nodes`.

| Value | Meaning |
|---|---|
| `auto` | (default) sparkrun decides based on node counts and CLI flags |
| `solo` | Forces single-node. Sets `min_nodes = max_nodes = 1`. |
| `cluster` | Forces multi-node. Requires 2+ hosts. |

### `min_nodes`

Minimum number of nodes required. Defaults to `1`.

On DGX Spark, each node has one GPU, so `min_nodes` is effectively the minimum GPU count.

### `max_nodes`

Maximum number of nodes supported. Defaults to unlimited.

Set `max_nodes: 1` for models or runtimes that do not support multi-node inference.

### `solo_only` / `cluster_only`

Boolean shorthand flags:

```yaml
solo_only: true     # equivalent to max_nodes: 1, mode: solo
cluster_only: true  # equivalent to min_nodes: 2, mode: cluster
```

## Configuration fields

### `defaults`

A flat dictionary of default parameter values. Every key is available as a `{placeholder}` in the command template and can be overridden at launch via CLI flags or `-o key=value`.

```yaml
defaults:
  port: 8000
  host: 0.0.0.0
  tensor_parallel: 1
  gpu_memory_utilization: 0.8
  max_model_len: 200000
  served_model_name: nemotron3-30b-a3b
```

#### Standardized defaults

The following keys are recognized by all runtimes and are automatically mapped to the correct runtime-specific flags. New runtimes are expected to support these keys for cross-runtime compatibility.

| Default key | CLI flag | Description |
|---|---|---|
| `port` | `--port` | Serve port |
| `host` | `-o host=X` | Bind address |
| `tensor_parallel` | `--tp` | Tensor parallelism degree |
| `gpu_memory_utilization` | `--gpu-mem` | GPU memory fraction (0.0–1.0) |
| `max_model_len` | `--max-model-len` | Maximum sequence length |
| `served_model_name` | `--served-model-name` | Model name exposed by the API (works for all runtimes) |

Any other key can appear in defaults — there is no fixed schema. Runtime-specific parameters (like `tool_call_parser`, `attention_backend`, etc.) are passed through as `{placeholder}` values for use in the command template.

**Config chain precedence** (highest wins):
1. CLI overrides (`--port 9000`, `--served-model-name my-model`, `-o key=value`)
2. Recipe defaults

### `env`

Environment variables injected into the container.

```yaml
env:
  VLLM_ALLOW_LONG_MAX_MODEL_LEN: "1"
  HF_TOKEN: "${HF_TOKEN}"
```

Shell variable references (`${VAR}`) are expanded from the **control machine's** environment when the recipe is loaded — forward secrets without hardcoding them.

## Metadata fields

### `metadata`

Descriptive and VRAM-estimation fields. Does not affect how the workload runs.

```yaml
metadata:
  description: NVIDIA Nemotron 3 Nano 30B (upstream NVFP4)
  maintainer: scitrera.ai <open-source-team@scitrera.com>
  model_params: 30B
  model_dtype: nvfp4
```

#### VRAM estimation fields

| Field | Type | Description |
|---|---|---|
| `model_params` | string | Parameter count: `"1.7B"`, `"30B"`, `"397B"` |
| `model_dtype` | string | Weight dtype: `bf16`, `fp16`, `fp8`, `nvfp4`, `int4`, `q8_0`, etc. |
| `kv_dtype` | string | KV cache dtype (defaults to `bfloat16`) |
| `num_layers` | int | Number of transformer layers |
| `num_kv_heads` | int | Number of key-value attention heads |
| `head_dim` | int | Dimension per attention head |
| `model_vram` | float | Override: total model weight VRAM in GB |
| `kv_vram_per_token` | float | Override: KV cache bytes per token |

sparkrun auto-detects most of these from HuggingFace, but metadata values take precedence — useful for quantized models.

## Runtime-specific fields

### `runtime_config`

A dictionary for runtime-specific configuration, primarily used by the `eugr-vllm` runtime:

```yaml
runtime_config:
  mods: [my-custom-mod]
  build_args: [--some-flag]
```

Unknown top-level keys are automatically swept into `runtime_config`.

## Automatic runtime detection

While explicitly setting `runtime` is recommended, sparkrun can infer the runtime from the `command` field when it is omitted. This is useful for quick experimentation or when adapting command snippets from documentation without worrying about which runtime value to set.

| Command prefix | Detected runtime |
|---|---|
| `vllm serve ...` | `vllm` (then resolved to `vllm-distributed` or `vllm-ray`) |
| `sglang serve ...` | `sglang` |
| `python -m sglang.launch_server ...` | `sglang` |
| `python3 -m sglang.launch_server ...` | `sglang` |
| `llama-server ...` | `llama-cpp` |

If the command doesn't match any of these patterns, the recipe falls through to `vllm` behavior by default.

An explicit `runtime` field **always wins** — command-hint detection only fires when `runtime` is omitted. Existing recipes that set `runtime` continue to work identically.

## Benchmark configuration

Recipes can include a `benchmark:` block to define default benchmark settings used by `sparkrun benchmark`:

```yaml
benchmark:
  framework: llama-benchy
  pp: [2048]
  depth: [0, 4096]
  prefix_caching: true
```

See the [benchmark command](/cli/benchmark/) for details on how benchmark configuration is resolved.

## Recipe discovery

sparkrun searches for recipes in this order:

1. **`@spark-arena/` shortcut** — `@spark-arena/UUID` expands to a Spark Arena URL.
2. **URL** — if the argument is an HTTP/HTTPS URL, the recipe is fetched directly and cached.
3. **`@registry/recipe-name` scoped lookup** — disambiguate recipes across registries using scoped syntax.
4. **Exact/relative file path** — if the argument is a path to an existing file.
5. **Current working directory** — sparkrun scans `.yaml`/`.yml` files in the CWD that are valid recipes.
6. **Registry search** — flat name lookup in configured custom registries, then recursive glob.

Filenames are matched with or without `.yaml`/`.yml` extensions.

## Command template substitution

The `command` field supports `{placeholder}` substitution from the config chain:

```yaml
defaults:
  port: 8000
  served_model_name: my-model

command: |
  vllm serve {model} --port {port} --served-model-name {served_model_name}
```

With `sparkrun run recipe -o port=9000`, this renders as:
```
vllm serve Qwen/Qwen3-1.7B --port 9000 --served-model-name my-model
```

The special placeholder `{model}` is always available from the top-level `model` field.
