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 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 and pulled the container as described in Getting Started.

Start the NIM#

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

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.

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

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.

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.

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 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 and 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.

Important

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.”

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 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 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 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 for what is collected, retention, and advanced options, and to NVIDIA’s 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 for measured runtimes, since a much lower value invites clients to retry against a NIM that cannot serve them yet.

Important

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.

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

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, and none of the preceding queue metric series are registered.

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.

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.

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.