AMD Inference Microservice deployment guide#
This guide gives step-by-step instructions to deploy the AMD Inference Microservice (AIM) container for the mit/boltz2 model in different environments. Follow these instructions to run the model on AMD accelerators. This guide assumes a MI300X accelerator on the target system.
This AIM has an API of its own, shaped by what the model takes and returns. See Testing your deployment for the endpoints and payloads of this model.
Prerequisites#
AMD Instinct™ GPU with ROCm support (e.g., MI300X, MI325X)
Docker installed and configured with GPU support
A host directory for the model cache, if the model downloads its weights at first start
1. Docker deployment#
1.1 Running the container#
Set AIM_PROFILE_ID to one of the profiles listed in the README. The runtime then uses that profile
and does not select a profile from the detected hardware.
mkdir -p /path/to/model-cache
docker run \
-e AIM_ID=mit/boltz2 \
-e AIM_PROFILE_ID=bentoml-mi300x-bf16-tp1-latency \
-v /path/to/model-cache:/workspace/model-cache \
--device=/dev/kfd --device=/dev/dri \
--group-add video --ipc=host \
-p 8000:8000 \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview
1.2 Customizing deployment with environment variables#
Customize your deployment with optional environment variables:
docker run \
-e AIM_ID=mit/boltz2 \
-e AIM_PROFILE_ID=bentoml-mi300x-bf16-tp1-latency \
-e AIM_CACHE_PATH=/workspace/model-cache \
-e AIM_LOG_LEVEL=INFO \
-e AIM_PORT=8080 \
-v /path/to/model-cache:/workspace/model-cache \
--device=/dev/kfd --device=/dev/dri \
--group-add video --ipc=host \
-p 8080:8080 \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview
Without AIM_PROFILE_ID, the runtime selects a profile from the detected hardware.
AIM_ACCELERATOR_COUNT changes the accelerator count that the selection uses. The runtime does not use it when
AIM_PROFILE_ID is set.
2. Model caching for production#
This AIM has no separate model download step. If the model downloads its weights, it does so at service
startup and keeps them in $AIM_CACHE_PATH, which defaults to /workspace/model-cache.
Mount a persistent volume at that path, as shown in section 1.1. Without the mount, the container downloads the weights again at each start.
If the cache must be at a different path, set AIM_CACHE_PATH and mount the volume at the same path:
docker run \
-e AIM_ID=mit/boltz2 \
-e AIM_PROFILE_ID=bentoml-mi300x-bf16-tp1-latency \
-e AIM_CACHE_PATH=/mnt/aim-cache \
-v /path/to/model-cache:/mnt/aim-cache \
--device=/dev/kfd --device=/dev/dri \
--group-add video --ipc=host \
-p 8000:8000 \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview
The first start is slower than later starts, because it downloads the weights. Size the volume for the model checkpoint, and keep it writable.
3. Kubernetes deployment#
3.1 Deployment#
It is possible to deploy AIM with Kubernetes. This section gives a sample Kubernetes deployment manifest.
Create deployment.yaml with the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: minimal-aim-deployment
labels:
app: minimal-aim-deployment
spec:
progressDeadlineSeconds: 3600
replicas: 1
selector:
matchLabels:
app: minimal-aim-deployment
template:
metadata:
labels:
app: minimal-aim-deployment
spec:
containers:
- name: minimal-aim-deployment
image: amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview
imagePullPolicy: Always
env:
- name: AIM_ID
value: "mit/boltz2"
- name: AIM_PROFILE_ID
value: "bentoml-mi300x-bf16-tp1-latency"
- name: AIM_CACHE_PATH
value: "/workspace/model-cache"
- name: AIM_LOG_LEVEL_ROOT
value: "INFO"
- name: AIM_LOG_LEVEL
value: "INFO"
- name: AIM_PORT
value: "8000"
ports:
- name: http
containerPort: 8000
resources:
requests:
memory: "64Gi"
cpu: "8"
amd.com/gpu: "1"
limits:
memory: "64Gi"
cpu: "8"
amd.com/gpu: "1"
startupProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 360
readinessProbe:
httpGet:
path: /readyz
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /healthz
port: http
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 30
volumeMounts:
- name: model-cache
mountPath: /workspace/model-cache
- name: ephemeral-storage
mountPath: /tmp
- name: dshm
mountPath: /dev/shm
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: minimal-aim-model-cache
- name: ephemeral-storage
emptyDir:
sizeLimit: 256Gi
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 32Gi
The startup probe gives the start up to one hour: 360 checks at 10-second intervals. That covers a first start that downloads weights, as described in section 2. The pod stays out of the service endpoints until this probe succeeds, so no request reaches a model that is still loading. A later start reads the cache and succeeds much sooner.
The readiness probe checks /readyz every 10 seconds. After three failed checks (30 seconds) Kubernetes takes the
pod out of the service endpoints, so a worker that fails after a successful start stops receiving requests until it
recovers. The liveness probe below is slower on purpose and only restarts the container.
Kubernetes runs the liveness probe only after the startup probe succeeds. The liveness probe then accepts 30 failed checks, which is five minutes, before it restarts the container. This tolerates a model that does more work after the health endpoint answers.
The memory and cpu values above are a starting point. Raise them if the profile uses more than one accelerator,
because this AIM starts one worker process per accelerator, and each worker holds its own copy of
the model.
Create the cache volume claim in pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minimal-aim-model-cache
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
Create a service configuration in the service.yaml file:
apiVersion: v1
kind: Service
metadata:
name: minimal-aim-deployment
labels:
app: minimal-aim-deployment
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 8000
selector:
app: minimal-aim-deployment
Customize the deployment further to fit your needs. Apply it to the Kubernetes cluster with kubectl:
kubectl apply -f . -n <YOUR_K8S_NAMESPACE>
4. Testing your deployment#
To test the deployment, execute an API call. First find the pod, then set up port forwarding.
kubectl port-forward service/minimal-aim-deployment 8000:80 -n <YOUR_K8S_NAMESPACE>
After port forwarding starts, the API is available locally at http://localhost:8000/.
4.1 Health check#
curl http://localhost:8000/healthz
The service answers with HTTP 200 when it is ready to accept requests. The startup probe and the liveness probe in section 3.1 use it.
/healthz answers a GET. An AIM can also give an endpoint that reports the state in more detail. That endpoint
takes a POST, and the section below names it if this AIM has one.
4.2 The endpoints#
Method |
Path |
Purpose |
|---|---|---|
|
|
Readiness probe built into BentoML. See section 4.1. |
|
|
The same readiness answer on the path that all AIMs share. It forwards to |
|
|
Lists the served model, |
|
|
Predict the structure of one biomolecular complex. |
The service is ready when /health answers HTTP 200. See section 4.9 for the start-up time.
4.3 Request body#
The request is POST /v1/inference with a JSON body. The whole request is nested under a top-level data object:
Field |
Type |
Default |
Purpose |
|---|---|---|---|
|
list of objects |
required |
The molecules of the complex. See the entity format below. |
|
string |
|
Name of the prediction. The service replaces each character that is not a letter, a digit, |
|
string |
|
|
|
integer |
|
Recycling steps of the trunk. Limit 10. |
|
integer |
|
Diffusion sampling steps. Limit 500. |
|
integer |
|
Number of structures to sample. Limit 4. |
|
integer |
|
Samples that run in one batch. Limit 5. |
|
number |
not set |
Diffusion step scale. Not set gives the model default. |
|
integer |
not set |
Seed for a repeatable prediction. Range 0 to 4294967295. See the section on the seed below. |
|
boolean |
|
Build the MSA with an MSA server. See section 4.8. |
|
string |
|
|
|
list of objects |
not set |
Boltz constraints, in the Boltz input format. |
|
list of objects |
not set |
Structure templates. Their |
|
list of objects |
not set |
Boltz properties. The only property that the service serves is |
|
boolean |
|
Add the molecular weight correction to the affinity value. Used only with an |
|
integer |
|
Diffusion sampling steps of the affinity pass. Limit 500. Used only with an |
|
integer |
|
Diffusion samples of the affinity pass. Limit 5. Used only with an |
|
string |
not set |
Boltz method label, for example |
|
integer |
|
Version of the Boltz input format. Only |
Each entry of sequences has one key, the molecule type, and an object as value. The format is the input format of
Boltz:
Molecule type |
Fields |
|---|---|
|
|
|
|
|
|
id is a chain identifier, or a list of identifiers for identical chains.
A protein chain without msa runs without an alignment unless use_msa_server is true.
Inline content only#
The request holds file content, not file paths. msa is either "empty" or the text of an A3M alignment that starts
with >. A template cif or pdb value is the mmCIF or PDB text. The service answers HTTP 400 for a value that is a
file path.
An A3M alignment must have at least one sequence line. Each sequence line holds upper-case residue letters and - for
gaps, and lower-case letters for inserts. The aligned length of each line (without the lower-case letters) must equal
the length of the protein sequence. The service answers HTTP 400 for another A3M.
Seed#
With seed, the service seeds all random number generators for the request and uses deterministic algorithms for it.
The same request with the same seed gives the same result, within one service process. The result is not guaranteed to
be the same in another service process, with another image, or on another accelerator. Without seed, the prediction
is not seeded and the service does not use deterministic algorithms. The response has the key seed (null without a
seed). Deterministic algorithms can make a prediction slower.
The first request with a seed after the service started takes about 1 minute longer than the later ones, because the
service compiles the model once more for the deterministic setting. A later request with a seed can compile once
more for each new input size in this setting. Requests without a seed do not compile after the start-up.
Limits#
The service refuses a request above these limits with HTTP 422, before any computation starts:
Limit |
Value |
|---|---|
Tokens of the whole request |
2364 |
|
4 |
|
10 |
|
500 |
|
5 |
|
500 |
|
5 |
Heavy atoms of the affinity binder |
128 |
A token is one residue or nucleotide of a polymer chain, counted for each chain copy, or one heavy atom of a ligand
given as smiles. A ligand given as a ccd code is not counted.
4.4 Example request#
curl http://localhost:8000/v1/inference \
-H "Content-Type: application/json" \
-d '{
"data": {
"name": "trp_cage",
"sequences": [
{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS"}}
],
"output_format": "mmcif"
}
}'
The same request in Python:
import requests
response = requests.post(
"http://localhost:8000/v1/inference",
json={
"data": {
"name": "trp_cage",
"sequences": [{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS"}}],
"output_format": "mmcif",
}
},
timeout=1800,
)
response.raise_for_status()
result = response.json()
print(result["structures"][0]["content"][:200])
4.5 Request with a ligand#
A ligand entry carries smiles or ccd instead of sequence. This request folds the protein together with toluene:
curl http://localhost:8000/v1/inference \
-H "Content-Type: application/json" \
-d '{
"data": {
"sequences": [
{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS"}},
{"ligand": {"id": "L", "smiles": "Cc1ccccc1"}}
]
}
}'
4.6 Request with a precomputed MSA#
To use an alignment that you have, give its text in msa. The alignment is inline A3M text. The service does not
contact an MSA server:
curl http://localhost:8000/v1/inference \
-H "Content-Type: application/json" \
-d '{
"data": {
"sequences": [
{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS", "msa": ">query\nNLYIQWLKDGGPSSGRPPPS\n"}}
]
}
}'
Constraints, templates and properties#
These fields use the input format of Boltz. Each example below is a request body that you send to
POST /v1/inference, for example with curl http://localhost:8000/v1/inference -H "Content-Type: application/json" -d @request.json.
A contact constraint asks for a distance of at most max_distance angstrom between two tokens. A token is
[chain id, residue number], and the residue number starts at 1:
{
"data": {
"name": "trp_cage_contact",
"sequences": [{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS"}}],
"constraints": [{"contact": {"token1": ["A", 1], "token2": ["A", 20], "max_distance": 8}}],
"sampling_steps": 10,
"recycling_steps": 1
}
}
A template is the text of a PDB file (pdb) or an mmCIF file (cif), and chain_id names the chain of your
sequences that it describes. The text is inline. The service does not read a path. The file must list the chain
sequence: SEQRES records in a PDB file, _entity_poly_seq in an mmCIF file. A file from the PDB has them. This Python
code sends the file template.pdb:
import requests
with open("template.pdb") as template_file:
template = template_file.read()
response = requests.post(
"http://localhost:8000/v1/inference",
json={
"data": {
"name": "peptide_with_template",
"sequences": [{"protein": {"id": "A", "sequence": "NLYIQW"}}],
"templates": [{"pdb": template, "chain_id": "A"}],
"sampling_steps": 10,
"recycling_steps": 1,
}
},
timeout=1800,
)
response.raise_for_status()
A properties entry asks for the affinity of a ligand. This one names the ligand L as the binder. See section 4.11
for the rules of the request and for the response:
{
"data": {
"name": "trp_cage_ligand",
"sequences": [
{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS"}},
{"ligand": {"id": "L", "smiles": "CCO"}}
],
"properties": [{"affinity": {"binder": "L"}}],
"sampling_steps": 10,
"recycling_steps": 1
}
}
4.7 Response#
A successful response has this shape:
{
"structures": [
{"record_id": "trp_cage_model_0", "format": "mmcif", "content": "data_...\n#\n..."}
],
"confidence": {
"trp_cage_model_0": {"confidence_score": 0.9, "ptm": 0.8, "iptm": 0.0, "complex_plddt": 0.9, "...": "..."}
},
"timing": {"runtime_s": 3.2},
"seed": null,
"runtime": {"mode": "affinity", "optimizations": "on", "...": "..."}
}
Key |
Purpose |
|---|---|
|
One entry per sample, best confidence first. |
|
Boltz-2 confidence metrics of each sample, keyed by |
|
|
|
The |
|
Only for a request with an |
|
The serving mode ( |
4.8 Status codes and the MSA server#
Code |
Meaning |
Retry |
|---|---|---|
|
Success. |
— |
|
The request is not valid, for example an unknown |
No. Correct the request. |
|
The request is above a limit. See section 4.3. |
No. Reduce the request. |
|
The queue is full. Each accelerator takes 3 requests at a time: one runs and two wait. |
Yes. The client must retry after a delay. |
|
The prediction failed. The message is generic. The service log holds the detail. |
Not with the same request. |
|
The MSA server did not answer within its time budget, or the request waited so long in the queue that it can no longer finish. The |
Yes. |
|
The request passed |
Not with the same request. |
Every error answer of POST /v1/inference has the JSON body {"error": "<text>"}, one object. A request to an unknown path gets the plain text answer Not Found from BentoML. The text of a 400 or 422 answer starts with Invalid request.. For an invalid request, the text has one short entry for each field, as <field>: <message>. It does not repeat the request. The text of a 500 answer is generic. The service log has the full detail of every error.
With use_msa_server: true, the protein sequences of the request are sent to an MSA server. The default is the
public ColabFold server at https://api.colabfold.com, which is a third-party service. Set
BOLTZ2_MSA_SERVER_URL to the address of an MSA server that you run, to keep sequences on your own network. Set it to
an empty value to turn the MSA server off: a request with use_msa_server: true then gets HTTP 400. A request with
use_msa_server: false, the default, never contacts an MSA server.
4.9 What to know before you deploy#
The model weights are downloaded at the first start. The structure checkpoint (2.3 GB) and the molecule
library go to $AIM_CACHE_PATH. The library is 1.8 GB, and its 1.9 GB archive stays in the cache. This is 6.0 GB.
The default profile runs in affinity mode. This mode also downloads the affinity checkpoint (2.1 GB): 8.0 GB in all.
Mount a persistent volume there, so that later starts do not download again.
The default profile runs in affinity mode, and this mode costs more than structure mode. The service downloads a
second checkpoint and keeps a second model loaded. It uses more accelerator memory, and the start-up is longer. A request
without an affinity property pays nothing extra. A deployment that needs only structure prediction can run in structure
mode: see section 4.11.
To use weights that you staged in that directory, set BOLTZ2_DOWNLOAD_ASSETS to false. The start then fails if a
file is missing. If downloads are on, the directory is read-only and a file is missing, the start also fails once.
The message names the directory. Mount it writable, or stage the weights. This model does not need a Hugging Face
token.
The service needs about 4 minutes to become ready when the cache holds the weights, and longer at a first start.
The download time depends on the network. In structure mode, a measured cold start took about 6.5 minutes. Affinity
mode adds a 2.1 GB download. In affinity mode, a measured cold start took about 7 min. With BOLTZ2_OPTIMIZATIONS=on,
the default, the service compiles the model at start-up with test predictions, and /health and /healthz answer HTTP 200 only after that. The
first request after the service is ready runs at normal speed, with one exception: the first request above 2048 tokens
compiles the model once more, which takes about 5 seconds extra. Later requests run at normal speed. Set the
readiness probe to allow for the start-up time.
BOLTZ2_OPTIMIZATIONS is the one performance setting. on, the default, uses the optimized kernels and the
start-up compilation. off runs the upstream Boltz code without these, which is slower and has no start-up
compilation.
Requests wait in a queue. Each accelerator runs one prediction at a time. The service accepts 3 requests per accelerator: one runs and two wait. A request over that gets HTTP 429 at once, and the client must retry it after a delay. Send requests one after the other, or use more replicas.
Each request has its own deadline. The deadline counts from the arrival of the request and ends before
BOLTZ2_REQUEST_TIMEOUT_SECONDS. A request that waited so long in the queue that it can no longer finish gets HTTP
503 with Retry-After, and the service does not start it. This happens when the request waits longer than
BOLTZ2_REQUEST_TIMEOUT_SECONDS minus 30 seconds (kept to send the answer) minus BOLTZ2_INFERENCE_RESERVE_SECONDS:
more than 1170 seconds with the profile values (1800 and 600). With use_msa_server: true, the MSA stage has its own time
limit (BOLTZ2_MSA_TIMEOUT_SECONDS) inside the request deadline, and the time that remains is kept for the
prediction. Set a client timeout that is not shorter than BOLTZ2_REQUEST_TIMEOUT_SECONDS, or the client gives up
before the service answers.
The prediction time grows with the size of the complex. A request of a few hundred tokens takes seconds. A request near the limit of 2364 tokens takes minutes.
4.10 Environment variables#
Variable |
Default |
Effect |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
MSA server for |
|
|
Whole-request cap. The request deadline is set inside it. |
|
|
Cap on the MSA-server work of one request. |
|
|
Time of the request deadline that the MSA stage cannot use, and the least time that a waiting request must have left to be started. |
|
|
Requests in the service before it answers 429. The value |
|
|
|
|
|
Download the weights at start-up when they are not in |
|
|
Directory of the model weights. |
The profile sets BOLTZ2_MODE (affinity), BOLTZ2_REQUEST_TIMEOUT_SECONDS (1800), BOLTZ2_MAX_CONCURRENCY (3) and BOLTZ2_ACCELERATOR (gpu). A value that you pass with docker run -e does not change them. The profile does not set the other variables in this table, so docker run -e changes them.
4.11 Affinity prediction#
Boltz-2 can predict the binding affinity of one ligand to the rest of the complex. The profile sets BOLTZ2_MODE to
affinity: the service loads the structure model and the affinity model, and serves both kinds of request. A request
without an affinity property gets the same answer as in structure mode. BOLTZ2_MODE is not case sensitive. Any
other value than structure or affinity stops the start-up with an error that lists the valid values.
docker run -e BOLTZ2_MODE=structure has no effect, because the profile value wins. To run in structure mode, mount
a copy of the profile file with BOLTZ2_MODE: structure over
/workspace/aim-runtime/profiles/mit/boltz2/bentoml-mi300x-bf16-tp1-latency.yaml in the container (docker run -v). The service then loads
the structure model only, downloads 2.1 GB less, and answers a request with an affinity property with HTTP 400 and the text
affinity is not served by this profile.
The affinity model costs the following, compared with structure mode: 2.1 GB more weights, more start-up time for the
warm-up, and more accelerator memory (the model stays resident). A request without an affinity property pays nothing
extra at run time. A request with one adds the affinity pass, affinity_s in timing. This takes about 3 to 7 seconds
when the cache is warm, for the complexes that were measured (up to about 1500 tokens).
With BOLTZ2_OPTIMIZATIONS=on, the start-up warm-up runs one complex with a ligand through both models. It compiles the
structure model, and it runs the affinity model with the triangle kernels and a CUDA graph of its score model, so the
first request of each kind does not compile. With BOLTZ2_OPTIMIZATIONS=off, only this one complex runs, in eager
mode. In structure mode with BOLTZ2_OPTIMIZATIONS=off, there is no warm-up.
BOLTZ2_AFFINITY_COMPILE=on is an option for BOLTZ2_MODE=affinity with BOLTZ2_OPTIMIZATIONS=on. It is off by
default, and the profile does not set it, so docker run -e BOLTZ2_AFFINITY_COMPILE=on turns it on. The service
then also compiles the Transition and token-transformer modules of the affinity model with TorchInductor. The warm-up
runs 3 affinity sampling steps and a second affinity request at the crop size, so the start-up takes longer. Set the
readiness probe to allow for it. runtime.affinity_compile_status shows the compile status of the affinity model.
The compiles of the affinity model appear in runtime.warmup under the labels affinity_<selector>. In structure mode, or with
BOLTZ2_OPTIMIZATIONS=off, the service ignores the option and writes one log line. A value other than on or off stops the start-up.
The affinity request#
Add one affinity entry to properties. binder is the id of one ligand in sequences:
{
"data": {
"name": "trp_cage_aspirin",
"sequences": [
{"protein": {"id": "A", "sequence": "NLYIQWLKDGGPSSGRPPPS"}},
{"ligand": {"id": "B", "smiles": "CC(=O)OC1=CC=CC=C1C(=O)O"}}
],
"properties": [{"affinity": {"binder": "B"}}],
"affinity_mw_correction": false,
"sampling_steps_affinity": 200,
"diffusion_samples_affinity": 5
}
}
The service predicts the structure first. It takes the best sample (rank 0) and runs the affinity model on it, as
boltz predict does. The affinity pass uses 5 recycling steps and one parallel sample. It crops the complex to 256
tokens and 2048 atoms around the binder, and it does not use templates. The settings recycling_steps,
sampling_steps, diffusion_samples and step_scale of the request apply to the structure pass. The step_scale
also applies to the affinity pass.
The binder must follow these rules:
The request has one
affinityentry, andbinderis oneidstring.The binder is a ligand with one chain, one copy and one residue.
The service refuses a ligand with a list of
idvalues for more than one copy. It also refuses accdlist of more than one code, and another ligand entry with the samesmilesorccd.The binder is a ligand, not a polymer chain. Boltz supports the affinity of small molecules to protein targets. For an RNA or DNA target, the values are not reliable.
The
smilesof the binder is a string that is not empty.A binder that is a
smilesligand has at most 128 heavy atoms in its largest fragment. Above that, the service answers HTTP 422.Boltz advises against more than 56 atoms, which was the maximum in training.
The service answers HTTP 400 for a request that breaks a rule, except the atom limit (HTTP 422). The text has the
reason, for example properties.affinity.binder must be the id of a ligand in sequences or
the affinity binder must be a ligand with one copy.
The affinity response#
The key affinity holds the values that boltz predict writes to its affinity_<id>.json file:
{
"affinity": {
"affinity_pred_value": 0.42,
"affinity_probability_binary": 0.87,
"affinity_pred_value1": 0.40,
"affinity_probability_binary1": 0.85,
"affinity_pred_value2": 0.44,
"affinity_probability_binary2": 0.89
},
"timing": {"runtime_s": 41.7, "affinity_s": 22.5}
}
Key |
Meaning |
|---|---|
|
Predicted binding affinity, as the base-10 logarithm of the IC50 in micromolar. A lower value means a stronger binder. Use it to compare known binders, for example in hit-to-lead work, and not to compare non-binders. |
|
Probability, from 0 to 1, that the ligand binds. Use it to separate binders from decoys, for example in a screen. |
|
The values of the first and the second member of the model ensemble. The two main keys are the values of the ensemble. |
The values in the example are for illustration. If the complex cannot be featurized for the affinity model, the service answers HTTP 400 and no structure is returned.
5. Advanced deployment scenarios#
5.1 Selecting a different profile#
Each profile of this AIM is pinned with AIM_PROFILE_ID. The profile identifier is the filename of the profile
without the .yaml extension. The README lists the available profiles and their accelerator counts.
docker run \
-e AIM_ID=mit/boltz2 \
-e AIM_PROFILE_ID=<PROFILE_ID> \
-v /path/to/model-cache:/workspace/model-cache \
--device=/dev/kfd --device=/dev/dri \
--group-add video --ipc=host \
-p 8000:8000 \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview
Section 1.1 describes how the runtime uses AIM_PROFILE_ID. If the profile uses a different number of accelerators
than the default profile, change the accelerator count under resources in the section 3.1 manifest to match.
6. Monitoring and troubleshooting#
6.1 Getting help on the commands#
A general help command is available as follows:
docker run \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview \
--help
A help command for specific subcommands is also available:
docker run \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview \
<subcommand> --help
6.2 Enabling detailed logging#
docker run \
-e AIM_LOG_LEVEL=DEBUG \
-e AIM_ID=mit/boltz2 \
-e AIM_PROFILE_ID=bentoml-mi300x-bf16-tp1-latency \
-v /path/to/model-cache:/workspace/model-cache \
--device=/dev/kfd --device=/dev/dri \
--group-add video --ipc=host \
-p 8000:8000 \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview
6.3 Checking profile selection results#
It is possible to check which profile AIM selects from the given environment variables.
docker run \
-e AIM_ID=mit/boltz2 \
-e AIM_ACCELERATOR_COUNT=1 \
-e AIM_PRECISION=bf16 \
-e AIM_ACCELERATOR_MODEL=MI300X \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview \
dry-run
6.4 List available profiles#
docker run \
amdenterpriseai/aim-instinct-model-mit-boltz2:2026.24.0-preview \
list-profiles
6.5 Common problems#
The container restarts during the first start. The manifest in section 3.1 gives the start one hour. If the model needs more, raise
failureThresholdon the startup probe. Mount a persistent cache, so that later starts skip the download.The container restarts after it answers on
/healthz. The liveness probe accepts five minutes of failed checks. If the model does more work after the health endpoint answers, and that work takes longer, raisefailureThresholdon the liveness probe.The container stops with a profile selection error. The error message gives the cause.
If
AIM_PROFILE_IDis set, the ID matches no profile of this AIM. Compare it with the README or the output oflist-profiles(see section 6.4).If the error lists profile IDs, the matching profiles must be selected explicitly. Set
AIM_PROFILE_IDto one of them.If no profile is compatible, the error counts the profiles per reason, such as
accelerator_mismatchorprecision_mismatch. Compare theDetected AcceleratorandAccelerator Countlines in the log, and anyAIM_PRECISIONorAIM_METRICyou set, with the profile table in the README. Or setAIM_PROFILE_IDto a profile from the README (see section 1.1).
/healthzdoes not answer yet. The service binds the port only after the model is loaded. Read the container logs to see how far startup got.
7. Security considerations#
Use Kubernetes secrets or environment variables for sensitive credentials
Implement appropriate network policies to restrict access to your deployment
The model cache holds downloaded model weights. Restrict write access to it