Skip to main content
reactor.yaml is the model spec. One file declares what the model is called, how the runtime loads it, how its image is built, and where it runs. reactor init writes it, and every other reactor command reads it. The file sits at the root of your workspace. A workspace is the directory tree rooted at a reactor.yaml, so you can run commands from a subdirectory. The CLI searches upward for the file and stops at the first directory that holds a .git entry, so the search never leaves your repository. The name is exactly reactor.yaml. There is no .yml spelling. reactor model register and reactor model update take --model-file <path>, which points at a different path rather than a different name, and reads a .json file as JSON. Check a file without contacting the server:

$schema

reactor.yaml
$schema declares which generation of the format you wrote. It is a marker, not a URL. Nothing is published at that address, so do not open it and do not point an editor at it for completion. Write $schema: reactor/v2 at the top of every new file. Older formats covers what happens to a file that predates it.

model:

What the model is called on the platform, and the hardware it asks for.

model.resources:

cpu and memory each take a request and a limit, both Kubernetes quantity strings such as 500m or 4Gi. The limit must be greater than or equal to the request. reactor.yaml is the only place to set them, because there are no equivalent CLI flags. Leave either key out and the Reactor API fills in a value. See Gotchas.

model.resources.gpu:

The scaffold requests one NVIDIA_B200.

Accelerator type codes

These are the codes model.resources.gpu.type accepts. The older model.gpu.type takes the same set. Write a code exactly as it appears here, because the Reactor API rejects any other string. The family is the only part of a code the CLI reads, and it comes from the prefix. A code that starts with AWS_TRAINIUM_ or AWS_INF_ is AWS Neuron hardware. Every other code is a GPU. This table copies a list the platform owns, so a platform release can add to it. Print the set your platform accepts today:
That command lists the accepted codes. It does not report free capacity. An accepted code is not a promise of hardware either, because the Reactor API checks region membership and per-region quota when you deploy. A code places the model at deploy time. reactor model register, reactor model update, and reactor model deploy send it to the Reactor API, which schedules the model on that hardware.
A code never gets you hardware locally. reactor run attaches no accelerator unless you pass --gpus yourself, and it never checks the code against your host. A workspace that declares NVIDIA_B200 runs on the CPU, with no warning.
A Neuron code is the one code that changes a local run, and it takes a flag away rather than adding one. Docker’s --gpus does not reach Neuron devices, so an AWS_TRAINIUM_ or AWS_INF_ code makes reactor run drop that flag and print a note. It drops the flag even when you passed --gpus all. Expose the device files your host has instead:

runtime:

How the runtime inside the container finds and loads your model code. The Go parser does not require import, so a file without it passes reactor validate and then fails when the container starts. Treat it as required. weights_path is read by three callers, each with its own precedence:
  • reactor run takes the bind mount from --weights, then $REACTOR_WEIGHTS_PATH, then runtime.weights_path. With none of the three set, nothing is mounted.
  • reactor model publish and reactor weights upload take the upload source from --weights, then runtime.weights_path, then ./weights.
  • Inside the container, the runtime reads $REACTOR_WEIGHTS_PATH, then runtime.weights_path, then falls back to its own cache directory.
A relative path resolves against the workspace root. See Weights.

build:

The image. reactor build renders a Dockerfile from this block in memory, so a scaffolded workspace ships no Dockerfile to maintain. Build the image walks through the workflow and the build secrets. The table below is the full field list. Both environment blocks reject six names the generated image sets for itself: HOST, LD_LIBRARY_PATH, PATH, PORT, PYTHONUNBUFFERED, and VIRTUAL_ENV.

build.run[]

Each step is either a plain string, which is the command, or a mapping: A mount takes three keys. type must be secret. id is required, and it matches the id you pass to reactor build --build-secret. target is the optional mount path.

deployment:

Where the model runs. Each entry takes two keys, and both are required: count: 0 on a named region is a deliberate scale to zero, and it is applied. An empty instances: list is different: it declares no plan, so the deploy warns and leaves per-region capacity unchanged. The scaffold requests one instance in us-west. The Reactor API checks quota and availability, and a deploy either applies the whole plan or changes nothing.

Key casing

The casing is not uniform across blocks, and the CLI accepts only one spelling. Copy the exact key:
  • model: uses hyphens for its multi-word keys: display-name, extra-args.
  • runtime:, build:, and deployment: use underscores: weights_path, runtime_version, base_image, cuda_version, python_version, system_packages, python_requirements, python_indexes, build_env, runtime_env.
build.runtime_version and model.display-name are the pair that catches people. Both are multi-word keys in the same file, spelled two different ways.
The parser drops keys it does not recognize instead of rejecting them. A misspelled key is ignored in silence, so a typo reads as “the field had no effect” rather than as an error. Run reactor validate and compare against the tables above.

Which commands read which blocks

model.description, model.public, model.resources.cpu, model.resources.memory, and model.resources.gpu.count reach the Reactor API through register and update. They have no effect on a local run. reactor run is the one command that does not search upward. It reads the reactor.yaml in the current directory only, so run it from the workspace root.

How it relates to the other files

reactor init scaffolds four files that work together, plus a weights directory:

config.yaml

config.yaml holds nothing Reactor reads. The runtime loads the file that runtime.config names and hands the parsed mapping to the model’s load() method. The contents and the format are yours. A relative path resolves against the workspace root. With no runtime.config, the model receives an empty mapping. reactor run -- --model.<key>=<value> merges overrides on top of the loaded file. Those overrides need runtime.config to be set, and fail with an error when it is not.

deployment.yaml

deployment.yaml is a standalone deployment plan, and it is not deprecated. One thing changed: reactor init no longer writes it. A fresh workspace’s plan is the deployment: block inside reactor.yaml, and reactor deploy init still writes a deployment.yaml for the CI path. When both exist, deployment.yaml wins: The scaffolded deployment.yaml ships an empty instances: list, which declares no plan. Add the file to a workspace that already carries a plan, and that plan stops being applied. Fill in the list to restore it.

A minimal file

The smallest file that both parsers accept, and that supports reactor build and reactor run:
reactor.yaml
Replace <runtime-version> with the release reactor init pinned in your workspace, or pick one from the runtime changelog. reactor model publish and reactor model deploy also need model.version. With no deployment: block, reactor model deploy warns and provisions nothing.

A fully populated file

Every key the format models, at its correct nesting.
reactor.yaml
base_image is left out on purpose. Setting it replaces the base that cuda_version picks, which makes cuda_version cosmetic.

Gotchas

A Dockerfile on disk switches most of build: off

When the workspace has a Dockerfile, reactor build builds that file verbatim. Nine fields then stop applying: base_image, cuda_version, python_version, system_packages, python_requirements, python_indexes, build_env, runtime_env, and run. The build prints a note naming the fields it dropped, so read it. platform and runtime_version still apply. Use reactor build --no-dockerfile to build from reactor.yaml instead.

base_image is not checked, but it is still constrained

The CLI uses the ref verbatim and validates nothing. But the generated image installs system packages with apt-get. A base that is not Debian- or Ubuntu-derived fails the build instead of the spec check.

model.display-name does nothing

The key parses, and then register and update both print that it is ignored and safe to remove. Set model.description instead.

An absent model.public is not public: false

The key has three states. On register, leaving it out registers the model private. On reactor model update, leaving it out keeps whatever the platform already holds, while an explicit true or false overwrites it. Write the value you mean.

build.runtime_version has no default

The scaffold pins a version, which makes the field look defaulted, but the parser has no fallback. A generated build with the key missing fails with an error naming it. Every workspace must carry it.

The CPU and memory defaults come from the Reactor API

They are not in this file. Leave cpu or memory out and the Reactor API fills in the request and the limit at deploy time. Those values can change, so treat them as a platform behavior rather than as a schema default. Set the keys yourself when a model needs a specific size.

Older formats

The format is versioned, and older files still load. Three shapes qualify: $schema: reactor/v1, the older apiVersion and kind header, and no header at all. Each is upgraded as it loads, and each earns a deprecation warning. The first generation also accepted a flat shape. There model was a string rather than a mapping, beside the scalar keys name, config, and weights_path. Two keys moved when the second generation landed. A deployment plan’s instances: now sits under deployment:, and the recorder’s recording: block sits under runtime:. Under $schema: reactor/v2 either key at the top level is a hard error rather than a silent drop, which is what catches a half-finished migration. Move the block down one level, or drop $schema and let the upgrade handle it. Write new files as reactor/v2.