> This page is for RFD4-Proteina, version 1.0.0 (Latest) (default).
> For other versions, use one of these documentation indexes:
> - 1.0.0 (Latest) (default): https://docs.nvidia.com/nim/bionemo/rfd4-proteina/1.0.0/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nim/bionemo/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nim/bionemo/_mcp/server.

# Configuration Alternatives at Runtime

This section describes how to run the RFD4-Proteina NIM with different
settings for

1. serving your own model checkpoints
2. deployment limits (request size, token count, step count, search cost)
3. execution backend and compile tuning
4. TMol metrics (refer to [Oracles](/nim/bionemo/rfd4-proteina/oracle) for the
   separate, mount-based refolding oracle)
5. general service settings and admission control

These instructions assume you have installed and set up
[Prerequisite Software](/nim/bionemo/rfd4-proteina/prerequisites) and pulled the container as
described in [Getting Started](/nim/bionemo/rfd4-proteina/getting-started).

## Start the NIM

```bash
export NGC_API_KEY=<Your NGC API Key>
export LOCAL_NIM_CACHE=~/.cache/nim
```

```bash
docker run --rm --name rfd4-proteina \
    --runtime=nvidia \
    --gpus 'device=0' \
    -e NGC_API_KEY \
    -v $LOCAL_NIM_CACHE:/opt/nim/.cache \
    -p 8000:8000 \
    --shm-size=16g \
    nvcr.io/nim/ipd/rfd4-proteina:1.0.0
```


Every environment variable that follows is passed the same way, with an
additional `-e VARIABLE=value` on the `docker run` command.

## Using Your Own Model Checkpoints

RFD4-Proteina serves from two checkpoints: the latent-diffusion trunk (the
velocity model, sometimes called the "denoiser") and a partial autoencoder
(encoder and decoder). Both are resolved from the manifest workspace at
`NIM_WORKSPACE_PATH` (default `/opt/nim/workspace`) unless named explicitly.
Serve your own weights (typically a fine-tune of the shipped architecture)
by mounting them into the container and naming them with two environment
variables:

| Variable | Default | Purpose |
|---|---|---|
| `NIM_DENOISER_CKPT` | unset, resolved from the workspace | Full path to the latent-diffusion checkpoint (the velocity trunk). |
| `NIM_AUTOENCODER_CKPT` | unset, resolved from the workspace | Full path to the partial-autoencoder checkpoint (encoder and decoder). |

Left unset, each is resolved by looking in `NIM_WORKSPACE_PATH` for the one
`*.ckpt` file that matches: autoencoder checkpoints are the ones whose name
contains `_ae_`, and the denoiser is whichever workspace checkpoint is not. That
only works when the workspace holds exactly one candidate of each kind, which
is true for the shipped image; set the variable explicitly whenever you mount
a different checkpoint, whether alongside the shipped one or in its place.

```bash
export NGC_API_KEY=<Your NGC API Key>
export LOCAL_NIM_CACHE=~/.cache/nim
export MY_CHECKPOINT_DIR=/data/rfd4-proteina-finetuned
```

```bash
docker run --rm --name rfd4-proteina \
    --runtime=nvidia \
    --gpus 'device=0' \
    -e NGC_API_KEY \
    -e NIM_DENOISER_CKPT=/checkpoints/my-finetuned-denoiser.ckpt \
    -e NIM_AUTOENCODER_CKPT=/checkpoints/my-finetuned_ae_.ckpt \
    -v $MY_CHECKPOINT_DIR:/checkpoints:ro \
    -v $LOCAL_NIM_CACHE:/opt/nim/.cache \
    -p 8000:8000 \
    --shm-size=16g \
    nvcr.io/nim/ipd/rfd4-proteina:1.0.0
```


Confirm which checkpoints were loaded from the startup log:

```
loaded RFProteina in 42.1s (denoiser=my-finetuned-denoiser.ckpt, autoencoder=my-finetuned_ae_.ckpt, device=cuda:0, execution=..., buckets=...)
```

### What Is and Is Not Checked

**A checkpoint you name must exist.** Setting `NIM_DENOISER_CKPT` or
`NIM_AUTOENCODER_CKPT` to a path that is not a file fails the NIM at startup
rather than falling back to whatever the workspace would have resolved on its
own:

```
FileNotFoundError: NIM_DENOISER_CKPT='/checkpoints/my-finetuned-denoiser.ckpt' names a
denoiser checkpoint that is not a file. The NIM will not fall back to the one
in /opt/nim/workspace: that would serve different weights than you asked for.
Check the path and that the volume is mounted, or unset NIM_DENOISER_CKPT to
use the shipped checkpoint.
```

This is deliberate. Quietly serving the shipped checkpoint instead would look
identical from the outside: the server starts, designs come back, metrics
look ordinary. A fine-tune-and-re-serve workflow would then measure the model
it was trying to replace. Leave both variables unset to use the shipped
checkpoints.

**The architecture is not checked up front.** Each checkpoint is loaded with
`strict=True`, so the model is rebuilt from that checkpoint's own saved
hyperparameters: any missing or unexpected weight key means the code has
drifted from whatever produced the checkpoint, and it is reported as a
PyTorch state-dict error at startup rather than a message from the NIM. This
also means a checkpoint trained with a materially different architecture (for
example, with the MSA module enabled, or a differently sized autoencoder)
fails loudly rather than loading partially initialized.

The Baker Laboratory's public `2026_10_15_*` checkpoints are one such case.
They widen the autoencoder's residue vocabulary from 33 to 147 classes and
its latent from 16 to 32 dimensions. As a result, they cannot be served
through `NIM_DENOISER_CKPT` and `NIM_AUTOENCODER_CKPT` on this release.

**No conversion is performed.** Supply PyTorch `.ckpt` files in the same
format the NIM ships: either a raw training checkpoint (the EMA weights are
picked up automatically from the paired `SimpleEMA` state) or one already
named `*-ema.ckpt`. The NIM does not train or fine-tune; produce the weights
with your own training code and serve them here.

<Note>
Accuracy and performance figures published for the RFD4-Proteina NIM describe
the shipped checkpoints. Designs from your own weights are yours to validate.
</Note>

## Deployment Limits

These bound what a request can ask for, independent of the wide, static
bounds enforced by the request schema itself: the schema stays wide so one
client can talk to deployments of different capacity, and what a *particular*
deployment accepts is a property of its GPU and its SLA.

| Variable | Default | Purpose |
|---|---|---|
| `NIM_MAX_NUM_STEPS` | `200` | Ceiling on `sampling.num_steps`, when a request pins it. |
| `NIM_MAX_TRUNK_PASSES` | `600` | Ceiling on trunk evaluations across one trajectory. Runtime scales with trunk passes rather than steps, and a custom `sampling.recycle_schedule` can raise them without raising the step count, so this is the limit that actually bounds a request's time on the GPU. The deepest named strategy, `thinking_high`, is 295. |
| `NIM_MAX_TOKENS` | `1024` | Ceiling on tokens in one design. Enforced after the input is parsed, since token count depends on the parsed structure. |
| `NIM_MAX_REQUEST_BYTES` | `33554432` (32 MiB) | Ceiling on the whole request body, enforced before the body is read. |
| `NIM_MAX_SEARCH_ROLLOUTS` | `256` | Ceiling on trajectories a search can roll out for scoring across one request (`beam_width x n_branch x intervals x num_replicates`). A schema-valid search can otherwise hold a GPU for hours; this bounds it per deployment. |

Every response reports the limits actually in force for that deployment in
`runtime.limits`, so a client does not need to know these variable names to
discover them.

<Note>
There is deliberately no ceiling on `sampling.num_replicates`. Replicates are
batched into one forward pass by default, so what a request can ask for is a
property of the GPU and the token count rather than a fixed number: a cap of
16 would refuse at 128 tokens a request the card would have served, and admit
at 1024 tokens one it cannot. The real detection is the `out_of_memory` (HTTP
413) answer, which names `sampling.batch_size` and `sampling.num_replicates`
as what the caller can change. Refer to [Replicates and
Batching](/nim/bionemo/rfd4-proteina/prerequisites#replicates-and-batching).
</Note>

A request that exceeds one of these limits, or whose input turns out to
require more GPU memory than is available, is answered with the appropriate
error code (`request_too_large` or `out_of_memory`); refer to the
[API Reference](https://docs.nvidia.com/nim/bionemo/rfd4-proteina/1.0.0/api-reference/generate-rfd-4-proteina-designs) for the full error-code table.

## Execution Backend and Compile Tuning

| Variable | Default | Purpose |
|---|---|---|
| `NIM_BACKEND` | `bioir` | Selects the execution backend: `oss_eager`, `oss_torch_compile`, or `bioir`. Refer to [Overview](/nim/bionemo/rfd4-proteina/overview#tooling) and [Performance](/nim/bionemo/rfd4-proteina/performance) for what each is. |
| `NIM_COMPILE_LENGTHS` | unset | Token-length buckets to reserve `torch.compile` for. Only meaningful for `oss_torch_compile`; ignored by the other backends. |

<Warning>
When switching a deployment from `oss_torch_compile` to `bioir`, make sure
`NIM_COMPILE_LENGTHS` is fully unset rather than set to an empty value: an
empty-but-set value is treated as an explicit (invalid) configuration rather
than "not configured."
</Warning>

## Oracle and TMol Metrics

The refolding oracle folds each design with an independent structure
predictor and scores it against eleven in-silico pass criteria
(`{criterion}.pass` rows in the response's `aggregated_metrics`). **This NIM
ships with no refolding oracle built in**: every image carries an inert
`reward.py` that declares none, and enabling one is a matter of mounting a
replacement. Refer to [Oracles](/nim/bionemo/rfd4-proteina/oracle) for the full
mount contract, the `NIM_ORACLE_PLUGIN`/`NIM_ALLOW_ORACLE_PLUGIN` variables,
and working reference implementations against hosted structure-prediction
NIMs.

| Variable | Default | Purpose |
|---|---|---|
| `NIM_ENABLE_TMOL_METRICS` | `true` | Enables TMol-based interface-energy metrics and reward keys. Independent of the refolding oracle; set `false` to opt out. |

A request tagging metrics or pass criteria that the mounted oracle does not
publish is rejected at request time with a clear error, rather than allowed
to run and silently score `-inf` mid-search. Refer to `/v1/tasks` and the
[API Reference](https://docs.nvidia.com/nim/bionemo/rfd4-proteina/1.0.0/api-reference/generate-rfd-4-proteina-designs) for which metric tags a running
deployment currently supports.

## General Service Settings

| Variable | Default | Purpose |
|---|---|---|
| `NIM_MODEL_VERSION` | the image's `VERSION` file | Overrides the version string reported in `runtime.nim_version` and by the model-info endpoints. |
| `NIM_LOG_LEVEL` | `INFO` | Service log level. |
| `NIM_GPU_IDS` | all visible | Comma-separated GPU indices for the service's worker pool to use. |
| `NIM_PROCESS_POOL` | `1` (enabled) | Set to `0` / `false` to disable the multi-GPU worker pool and serve from a single process. |
| `NIM_WORKSPACE_PATH` | `/opt/nim/workspace` | Working directory the pipeline uses for scratch files. |
| `NIM_WORKER_STARTUP_TIMEOUT` | `1200` | Seconds to wait for each GPU worker process to load the model before startup fails. |
| `NIM_MAX_QUEUED_REQUESTS` | `10` | Requests allowed to wait beyond the ones already running, before this NIM's own gate answers `503`. Stands down in favor of [Admission Control](#admission-control) when that is enabled. |

## NIM Telemetry

NIM Telemetry collects minimal host and NIM information to help improve
performance, reliability, and compatibility across deployments. It is
disabled by default and, when enabled, stays disabled on RTX GPUs unless
explicitly opted in.

| Variable | Default | Purpose |
|---|---|---|
| `NIM_TELEMETRY_MODE` | `0` | `0` disables telemetry. `1` enables baseline collection (hardware and NIM metadata). |
| `NIM_TELEMETRY_ENABLE_ON_RTX` | `false` | Set to `true` to allow telemetry collection on RTX GPUs (for example RTX PRO 6000 Blackwell). Only takes effect when `NIM_TELEMETRY_MODE` is not `0`; on RTX hardware, telemetry stays disabled even with `NIM_TELEMETRY_MODE=1` unless this is also set. |
| `NIM_TELEMETRY_ENABLE_LOGGING` | `true` | Enables logging for telemetry operations. Only relevant when `NIM_TELEMETRY_MODE=1`. |

On an RTX GPU with telemetry requested but not opted in, the startup log
reports it explicitly:

```
RTX GPU detected: NVIDIA RTX PRO 6000 Blackwell Workstation Edition
Telemetry collection disabled
```

Refer to [NIM Telemetry
Settings](https://developer.nvidia.com/docs/nim-tools/latest/templates/nimlib/nim-telemetry.html)
for what is collected, retention, and advanced options, and to [NVIDIA's
Privacy Policy](https://www.nvidia.com/en-us/about-nvidia/privacy-policy/).

## Admission Control

By default, every request waits its turn for a free GPU, bounded only by
`NIM_MAX_QUEUED_REQUESTS` (`10`): beyond that, a request is rejected on
arrival with `503` rather than joining a queue it is unlikely to clear. In a
saturated deployment, nothing outside the process can see how deep that queue
is, so a client cannot distinguish a slow design from one that has not
started.

Setting `NIM_ENABLE_QUEUE_METRICS` to `true` hands queueing to nimlib's own
admission-control middleware (nimlib >= 0.22.0) instead. It bounds
concurrency at `NIM_MAX_CONCURRENT_REQUESTS`, sheds requests with `503` and a
`Retry-After` header after `NIM_QUEUE_TIMEOUT_SECONDS` elapses or
`NIM_MAX_QUEUE_DEPTH` is exceeded, and publishes queue-depth metrics on
`/v1/metrics`. This NIM's own queue gate (`NIM_MAX_QUEUED_REQUESTS`,
described earlier) stands down the moment admission control is enabled, so a
request is never waiting in two places that disagree about how many are
ahead of it.

| Variable | Default | Purpose |
|---|---|---|
| `NIM_ENABLE_QUEUE_METRICS` | `false` | Set to `true` to enable nimlib's admission control and publish the request queue metrics on `/v1/metrics`. Requires `NIM_MAX_CONCURRENT_REQUESTS`. |
| `NIM_MAX_CONCURRENT_REQUESTS` | unset | Number of requests served at the same time. This NIM runs one request per GPU, so set it to the number of GPUs the container can use (the size of `NIM_GPU_IDS`, or every visible GPU). Required when `NIM_ENABLE_QUEUE_METRICS` is enabled; **the container refuses to start when the two values disagree.** |
| `NIM_MAX_QUEUE_DEPTH` | unbounded | Number of requests allowed to wait for a free GPU under admission control. Beyond this number, a request is rejected on arrival with `503` instead of queueing. Both unset and `0` mean unbounded. |
| `NIM_QUEUE_TIMEOUT_SECONDS` | `300` | How long a request waits for a free GPU before it is rejected with `503`. `0` waits indefinitely. |
| `NIM_QUEUE_RETRY_AFTER_SECONDS` | `30` | Value sent in the `Retry-After` header of a rejected request, jittered so a rejected batch does not retry at the same moment. Raise it to match observed design time; refer to [Performance](/nim/bionemo/rfd4-proteina/performance) for measured runtimes, since a much lower value invites clients to retry against a NIM that cannot serve them yet. |

<Warning>
Unlike some other bioNeMo NIMs, RFD4-Proteina does **not** accept
`NIM_GPU_ACQUIRE_TIMEOUT_SECONDS`. This NIM's GPU worker pool waits
indefinitely for a free GPU after a request holds an admission-control permit,
rather than enforcing a second, shorter timeout of its own: a timed-out wait
would not stop the design in progress, since the worker keeps the GPU and
finishes anyway while the caller has already been told it failed.
</Warning>

```bash
export NGC_API_KEY=<Your NGC API Key>
export LOCAL_NIM_CACHE=~/.cache/nim
```

```bash
docker run --rm --name rfd4-proteina \
    --runtime=nvidia \
    --gpus '"device=0,1"' \
    -e NGC_API_KEY \
    -e NIM_ENABLE_QUEUE_METRICS=true \
    -e NIM_MAX_CONCURRENT_REQUESTS=2 \
    -e NIM_MAX_QUEUE_DEPTH=8 \
    -e NIM_QUEUE_TIMEOUT_SECONDS=600 \
    -e NIM_QUEUE_RETRY_AFTER_SECONDS=60 \
    -v $LOCAL_NIM_CACHE:/opt/nim/.cache \
    -p 8000:8000 \
    --shm-size=16g \
    nvcr.io/nim/ipd/rfd4-proteina:1.0.0
```


The following table describes requirements admission control cannot infer
for you.

| Requirement | Reason |
| --- | --- |
| `NIM_MAX_CONCURRENT_REQUESTS` must equal the number of GPUs this NIM serves from. | Concurrency is enforced by the worker pool, one request per GPU, regardless of this setting. A larger value only moves the surplus queue to where nimlib's metrics cannot see it (`nim_request_queue_depth` would read `0` while requests pile up on the inner gate); a smaller value idles GPUs. The container refuses to start when the two disagree, rather than report a limit that does not hold. |
| Health and documentation endpoints bypass admission control. | A saturated NIM continues to answer `/v1/health/ready` and to serve `/docs`. The predict endpoint is subject to admission control. |

While admission control is enabled, the following Prometheus series are
published on `/v1/metrics`:

| Metric | Description |
| --- | --- |
| `nim_request_queue_depth` | Number of requests waiting for a GPU. |
| `nim_requests_running` | Number of requests running on a GPU. |
| `nim_request_queue_time_seconds` | Histogram of time spent waiting for a GPU. |
| `nim_requests_shed_total` | Requests rejected with `503`, labeled `queue_full` or `timeout`. |

<Note>
Admission control is disabled unless `NIM_ENABLE_QUEUE_METRICS` is set. When
it is unset, this NIM's own queue gate (`NIM_MAX_QUEUED_REQUESTS`) governs
request handling exactly as described in
[Behavior Under Load](#behavior-under-load), and none of the preceding queue
metric series are registered.
</Note>

## Behavior Under Load

A request arriving with every GPU worker busy and the queue full is answered
with **HTTP 503** (`service_busy`), not 429: the NIM is not rate-limiting a
particular caller, it has no free capacity, so the caller should retry
elsewhere. Which queue governs this, this NIM's own
(`NIM_MAX_QUEUED_REQUESTS`) or nimlib's admission control
(`NIM_MAX_QUEUE_DEPTH`), depends on whether `NIM_ENABLE_QUEUE_METRICS` is set;
refer to [Admission Control](#admission-control).

A request whose GPU memory is exhausted is answered with **HTTP 413**
(`out_of_memory`), a 4xx status: the same job on the same GPU will exhaust it
again, so retrying unchanged will not help. Memory grows with the batch size,
with the square of the token count, and with search width, so what can change
is `sampling.batch_size` (`1` serves the replicates one at a time), the input
size, or the search width. The error names the batch size and token count it
failed at. Because `batch_size` defaults to `num_replicates`, a request that
raised its replicate count without setting a batch size raised its peak
memory with it; refer to [Replicates and
Batching](/nim/bionemo/rfd4-proteina/prerequisites#replicates-and-batching).

A client that disconnects before its response is ready is recorded as
**HTTP 499** (`client_gone`) rather than a server error: since nobody
receives that status by definition, it exists only so the drop is not
counted against the NIM's error rate. This matters more for RFD4-Proteina
than for a single forward pass: an abandoned beam search can hold a GPU for
minutes producing a response that nothing will read.