Problem
After a scheduled secret rotation, ONNX Runtime instances deployed in several cloud regions start failing to load model artifacts. The runtime logs contain authentication errors, and the model loading step hangs or aborts. This outage appears only after the rotation completes, despite the secret manager entries being updated correctly.
Root Cause
ONNX Runtime uses the Ort::SessionOptions AddExternalFile API to fetch model files from a private object store (e.g., Amazon S3, Azure Blob, GCS). The credentials are obtained from a cloud‑native secret manager (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) via a side‑car credential‑provider process. The rotation workflow replaces the secret value but does not invalidate the cached credentials held by the provider. Because each region runs its own provider process, the following sequence occurs:
- Rotation updates the secret value in the manager.
- The provider process continues to serve the old, now‑revoked token until it receives a
SIGHUPor restarts. - ONNX Runtime instances, which keep a long‑lived HTTP connection pool to the object store, reuse the stale token, resulting in
403 Forbiddenor401 Unauthorizedresponses. - Only after a manual restart of the provider (or the entire pod) do the new credentials propagate, restoring access.
The root cause is therefore a missing cache‑invalidation mechanism for secret‑derived credentials in the multi‑region deployment.
Debug
Below is a typical log excerpt from an ONNX Runtime pod after rotation:
2026-09-30T12:15:42.317Z [ERROR] OrtSession::LoadModelFromFile: Failed to open model file s3://ml-models/us-east-1/model.onnx
2026-09-30T12:15:42.317Z [ERROR] S3Client::GetObject: HTTP/1.1 403 Forbidden
2026-09-30T12:15:42.317Z [INFO] RequestId: 3F2A1B7C5D6E7F8G9H0I
2026-09-30T12:15:42.317Z [DEBUG] CredentialProvider: Using cached token expiring at 2026-09-30T12:10:00Z
Key observations:
- The token expiration timestamp (
12:10:00Z) is earlier than the current time, indicating a stale token. - No reload of the secret is attempted after the initial load.
- The same error repeats across all regions, confirming a systemic caching issue.
Diagnostic steps performed:
- Inspect the provider process:
- Check the provider’s internal cache file (if any):
- Force a token refresh manually:
- Verify that ONNX Runtime picks up the new token:
$ ps -ef | grep credential-provider
root 12457 12455 0 12:00 ? 00:00:00 /usr/local/bin/credential-provider --region us-east-1
$ cat /var/run/credential-provider/cache.json
{
"access_key_id": "AKIA************",
"secret_access_key": "**************",
"session_token": "FQoGZXIvYXdzE...",
"expiration": "2026-09-30T12:10:00Z"
}
$ kill -SIGHUP 12457 # sends reload signal
$ journalctl -u credential-provider -n 20
...
2026-09-30T12:16:05.123Z INFO Refreshing credentials from Secrets Manager
2026-09-30T12:16:05.456Z INFO New token expires at 2026-09-30T18:00:00Z
$ curl -v -H "Authorization: Bearer $(cat /var/run/credential-provider/cache.json | jq -r .session_token)" \
https://s3.us-east-1.amazonaws.com/ml-models/us-east-1/model.onnx -o /dev/null
* Trying 52.216.0.1...
* Connected to s3.us-east-1.amazonaws.com (52.216.0.1) port 443 (#0)
> GET /ml-models/us-east-1/model.onnx HTTP/1.1
> Host: s3.us-east-1.amazonaws.com
> Authorization: Bearer AQoDYXdzE...
> User-Agent: curl/7.88.1
> Accept: */*
< HTTP/1.1 200 OK
...
After the manual reload, the model loads successfully, confirming the cache‑invalidation hypothesis.
Solution
The fix consists of two parts:
- Update the credential‑provider to watch the secret manager for changes and automatically refresh the cache. This can be achieved by enabling the native
SecretsManagerEventBridgeintegration (AWS) orKeyVaultEventGrid(Azure) and configuring the provider to listen forSecretRotationevents. - Modify the ONNX Runtime deployment to restart the provider process on rotation without affecting the model serving containers. This is done by adding a sidecar container that watches the secret version and sends
SIGHUPto the provider when a new version is detected.
Below are the relevant configuration changes.
Before (static provider, no event handling)
apiVersion: v1
kind: Pod
metadata:
name: onnx-runtime-us-east-1
spec:
containers:
- name: onnx-runtime
image: myrepo/onnx-runtime:2.1
env:
- name: MODEL_URI
value: s3://ml-models/us-east-1/model.onnx
- name: credential-provider
image: myrepo/credential-provider:1.0
args: ["--region", "us-east-1"]
env:
- name: SECRET_ID
value: arn:aws:secretsmanager:us-east-1:123456789012:secret:ml-credentials
After (event‑driven refresh and sidecar watcher)
apiVersion: v1
kind: Pod
metadata:
name: onnx-runtime-us-east-1
spec:
containers:
- name: onnx-runtime
image: myrepo/onnx-runtime:2.1
env:
- name: MODEL_URI
value: s3://ml-models/us-east-1/model.onnx
- name: credential-provider
image: myrepo/credential-provider:1.2
args: ["--region", "us-east-1", "--enable-event-listener"]
env:
- name: SECRET_ID
value: arn:aws:secretsmanager:us-east-1:123456789012:secret:ml-credentials
- name: EVENT_BUS_ARN
value: arn:aws:events:us-east-1:123456789012:event-bus/default
- name: secret-watcher
image: bitnami/kubectl:1.27
command: ["/bin/sh", "-c"]
args:
- |
while true; do
VERSION=$(aws secretsmanager describe-secret --secret-id $SECRET_ID \
--query 'VersionIdsToStages' --output text | awk '{print $1}')
if [ "$VERSION" != "$(cat /tmp/last_version 2>/dev/null)" ]; then
echo $VERSION > /tmp/last_version
kill -SIGHUP $(pidof credential-provider)
fi
sleep 30
done
env:
- name: SECRET_ID
value: arn:aws:secretsmanager:us-east-1:123456789012:secret:ml-credentials
Key changes:
- The provider now runs with
--enable-event-listener, subscribing to the EventBridge rule that fires on secret rotation. - A lightweight
secret-watchersidecar polls the secret version and sendsSIGHUPto the provider when a change is detected, ensuring a refresh even if the event delivery is delayed. - No changes are required in the ONNX Runtime container; it continues to read the token from the shared cache file.
Verify
After applying the updated manifest, perform the following checks:
- Confirm the provider receives rotation events:
- Validate that the cache file reflects the new expiration:
- Run a model load test:
- Check that no authentication errors appear in the runtime logs for at least one full rotation interval.
$ kubectl logs onnx-runtime-us-east-1 -c credential-provider | grep "Received rotation event"
2026-09-30T13:00:02.112Z INFO Received rotation event for secret ml-credentials
2026-09-30T13:00:02.415Z INFO Refreshing credentials from Secrets Manager
$ cat /var/run/credential-provider/cache.json | jq .expiration
"2026-09-30T19:00:00Z"
$ python - <<'PY'
import onnxruntime as ort
sess = ort.InferenceSession("s3://ml-models/us-east-1/model.onnx")
print("Model loaded, inputs:", sess.get_inputs())
PY
Model loaded, inputs: [Name: input_1, Shape: [1, 3, 224, 224], Type: float]
Prevent
To avoid recurrence, adopt the following guardrails:
- Enable automatic credential refresh in the provider library. Ensure the
--enable-event-listenerflag is part of every deployment. - Instrument health checks that verify the provider can reach the secret manager. Example endpoint:
/healthz?type=credentialreturning HTTP 200 only when a fresh token (< 5 min to expiry) is cached. - Set up alerts on authentication error spikes. Use a log‑based metric that counts
403 Forbiddenresponses from the object store with a labelsource=onnx-runtime. - Document the rotation schedule and ensure the sidecar watcher runs with a short poll interval (≤ 30 s) in case event delivery is delayed.
- Run integration tests that simulate secret rotation. The test suite should rotate a dummy secret, trigger the watcher, and assert that a model load succeeds without pod restarts.
Related Topic Hub: Model Serving Troubleshooting Hub
FAQ
- Why does the issue appear only after rotation and not during initial deployment?
During the initial start, the provider fetches a fresh token and caches it. Rotation replaces the secret but the provider continues to serve the cached token until it receives a reload signal, causing the failure. - Can I rely solely on the provider’s built‑in event listener without a sidecar?
The built‑in listener works if the event bus delivers events reliably. Adding a sidecar watchdog provides a safety net for missed events and is recommended for production multi‑region setups. - What if my object store uses IAM roles instead of static credentials?
When using IAM roles (e.g., EC2 instance profile or EKS service account), the provider should request temporary credentials from the metadata service. The same caching issue applies; ensure the provider refreshes tokens based on theExpirationfield. - How do I test the rotation flow in a staging environment?
Create a test secret, configure the provider to point to it, rotate the secret manually via the cloud console or CLI, and verify that the provider logs a refresh and that ONNX Runtime can still load the model. - Is there a performance impact of refreshing credentials on every request?
No. The provider refreshes only when the token is near expiration or when a rotation event is received. Regular inference requests continue to use the cached token, preserving low latency.