---
title: Set Instance readiness deadlines
description: Bound how long OMENative waits for a new Instance to become Ready with instanceReadyTimeout and the stuck-pod and unschedulable grace periods, and see what OME does when one runs out.
since: v1.3
---
On [OMENative](../../concepts/omenative/overview.md) components, each attempt to create, update or restart an [Instance](../../concepts/omenative/instances.md) has a deadline to become Ready. A bad image, a crashing server or a pod that no node can take then ends in a `Failed` Instance that says why, instead of a wait with no end. The deadline acts on the whole Instance: one pod, or a leader and its workers.
The `ome-resources` chart sets all three limits by default. You'll give one component more time, watch its Instance against the deadline, and change the cluster defaults.
- OME v1.3 or later, installed with the `ome-resources` chart, and `kubectl` access that lets you create namespaces and InferenceServices. See [Install OME](../../getting-started/install.md).
- The ClusterBaseModel `qwen3-0-6b` in the `Ready` state, and the ClusterServingRuntime `srt-qwen3-0-6b`, from [Serve your first model](../../getting-started/serve-your-first-model.md).
- A node where the model is `Ready`, with a free NVIDIA GPU, 10 CPUs and 30 GiB of memory for the engine pod. If the InferenceService from Serve your first model still holds the GPU, delete it with `kubectl delete inferenceservice qwen3-0-6b -n qwen3-0-6b`, and keep the model and the runtime.
- The [kubectl ome](../../reference/kubectl-ome/overview.md) plugin, for Step 2.
- For [Change the cluster defaults](#change-the-cluster-defaults): access to the `ome` Helm release, and `jq`.
## How the deadlines work
| Setting | What it does | Where you set it | Chart default |
| --- | --- | --- | --- |
| `instanceReadyTimeout` | Time for an attempt to become Ready, including scheduling, the image pull and the server start. With none set anywhere, a stuck Instance waits for you. | A component's `lifecycle`, or the cluster | `30m` |
| `stuckPodGracePeriod` | Fails the attempt early when a container or init container is stuck, as in `ImagePullBackOff`, and its pod is older than this. Unset, only the deadline fails it. | The cluster | `60s` |
| `unschedulableGracePeriod` | Fails the attempt when the scheduler can't place a pod for this long. Unset, the pod waits until you free capacity. | The cluster | `15m` |
A component's value in the InferenceService wins over the runtime's, and either wins over the cluster's. The same timeout sets the deadline of an [Instance migration](../../concepts/omenative/migration-and-transient-scale.md#deadline), and a [drain](recover-stuck-deletions.md) that runs past it raises `DrainOverdue`. OME also derives a multi-pod Instance's [gang schedule timeout](../../concepts/serving/gang-scheduling.md#bound-the-gang-schedule-timeout) from it.
The clock stops while an attempt waits on something outside the Instance: a scheduling gate, a quota refusal, an unschedulable pod, an unreachable node or a [paused rollout](../roll-out-changes/pause-and-resume-a-rollout.md#what-a-pause-holds). When the wait ends, the attempt gets a full timeout again. A changed timeout applies to new attempts, and to running ones that have no deadline yet. A changed grace period applies at once. [Operations](../../concepts/omenative/instances.md#operations) has the details.
## Step 1: Give a component more time
Create a namespace for the example:
```bash
kubectl create namespace qwen3-deadlines
```
```output
namespace/qwen3-deadlines created
```
This InferenceService runs the engine on OMENative and gives it 45 minutes, to leave room for a first image pull on a new node:
```yaml title="qwen3-0-6b-deadlines.yaml"
apiVersion: ome.io/v1beta1
kind: InferenceService
metadata:
name: qwen3-0-6b
namespace: qwen3-deadlines
spec:
deploymentMode: OMENative
model:
name: qwen3-0-6b
runtime:
name: srt-qwen3-0-6b
engine:
minReplicas: 1
maxReplicas: 1
lifecycle:
instanceReadyTimeout: 45m
```
Apply it:
```bash
kubectl apply -f qwen3-0-6b-deadlines.yaml
```
```output
inferenceservice.ome.io/qwen3-0-6b created
```
To set a default for every InferenceService that uses a runtime, put the same `lifecycle` block under the runtime's `engineConfig`, `decoderConfig` or `routerConfig`.
Once OME has created the engine's InferenceReplica, `qwen3-0-6b-engine`, check the timeout that applies:
```bash
kubectl get inferencereplica qwen3-0-6b-engine -n qwen3-deadlines \
-o jsonpath='{.status.conditions[?(@.type=="InstanceReadyTimeoutUnconfigured")].message}{"\n"}'
```
```output
Instance readiness deadline is 45m0s
```
Without a timeout, the condition is `True`, and its message starts with `no instanceReadyTimeout is set`: see [The condition says no timeout is set](#the-condition-says-no-timeout-is-set). OME creates an InferenceReplica only for components on OMENative. If kubectl can't find it, see [Opt in to OMENative](../../concepts/architecture/deployment-modes.md#opt-in-to-omenative).
## Step 2: Watch the Instance against its deadline
Right after you apply the InferenceService, OME starts a Create operation for its Instance. Show the Instance in the wide format, which includes the operation's times:
```bash
kubectl ome instance status qwen3-0-6b 0 --component engine -n qwen3-deadlines -o wide
```
These rows show the attempt and its deadline:
| Row | What it shows |
| --- | --- |
| `phase` | `Creating` while the Instance starts, then `Ready`, or `Failed` if a limit runs out. |
| `operation started`, `operation deadline` | When the attempt began, and when it fails if it isn't Ready, in UTC. Here they're 45 minutes apart. While the clock is stopped, the deadline shows `-`. |
| `op hold` | What the attempt waits on, after `waiting=`. |
| `failure pod`, `failure reason`, `failure time` | Which pod failed, why and when. |
Once the Instance is Ready, the operation rows are gone.
### When a limit runs out {#when-a-deadline-runs-out}
OME marks the Instance `Failed`, and the `failure reason` row says why:
| Reason | What it means | What to do |
| --- | --- | --- |
| `ImagePullBackOff`, `ErrImagePull`, `InvalidImageName` | The image can't be pulled. | Fix the image, and publish a new revision. |
| `CreateContainerConfigError` | A Secret or ConfigMap that the container needs is missing, or its configuration is broken. | Fix the configuration, and publish a new revision. |
| `CrashLoopBackOff`, `CreateContainerError`, `RunContainerError` | The container can't start, or keeps exiting. | Read the logs. With a `60s` `stuckPodGracePeriod`, one crash after the pod's first minute fails the attempt. Raise it for a server that needs a restart or two. |
| `ContainersNotReady` | The pod runs, but its containers never passed their readiness checks. | Read the logs. If the model needs longer to load, raise `instanceReadyTimeout`. |
| `ReadinessGateNotSatisfied` | The containers are ready, but a readiness gate keeps the pod out of its Service. | Run `kubectl describe pod` to see which gate stays `False`. |
| `Unschedulable` | The scheduler couldn't place the pod within `unschedulableGracePeriod`. | Free capacity, or change the spec. |
| `DeadlineExceeded` | The deadline passed with none of the evidence above. | Read the logs. If the start needs longer, raise `instanceReadyTimeout`. |
What OME does next depends on the cause and the operation:
- Only an image or configuration error counts against the revision. OME retries the revision with backoff, then holds it until you publish a fixed one or [release it](../roll-out-changes/release-a-held-revision.md).
- A failed Create or single-pod update then starts a new attempt. If the revision isn't at fault and the pods all ran on one node, OME can [build it on another node](../../concepts/omenative/migration-and-transient-scale.md#automatic-relocation), up to 3 times with the chart.
- When a multi-pod update fails under the default SurgeThenDrain strategy, OME removes the replacement, and the Instance goes back to `Ready` on its old revision. Under `RecreatePod` or an in-place strategy, the Instance stays `Failed` until a new revision reaches it, and a reset skips it.
- A failed restart leaves the Instance `Failed`, with its pods, until you [reset it](reset-failed-instances.md) or a new revision reaches it.
[What OME does with a failed Instance](../../concepts/omenative/instances.md#what-ome-does-with-a-failed-instance) has the details. OME also records an `InstanceFailed` Warning event on the InferenceService. List those events:
```bash
kubectl get events -n qwen3-deadlines --field-selector reason=InstanceFailed
```
While no Instance has failed, kubectl prints:
```output
No resources found in qwen3-deadlines namespace.
```
Each message names the Instance, its pod and the reason, and often what OME did next.
## Change the cluster defaults
Set the cluster values under `ome.controller.lifecycle`, in the values file you install `ome-resources` with:
```yaml title="values.yaml"
ome:
controller:
lifecycle:
instanceReadyTimeout: 30m
stuckPodGracePeriod: 2m
unschedulableGracePeriod: 15m
```
Give each duration a unit, as in `90s` or `1h`. A bare number, like `30`, stops new controller pods from starting: see [The controller doesn't start after you change the settings](#the-controller-doesnt-start-after-you-change-the-settings). To turn a setting off, set it to `null`.
With no timeout at all, a migration request fails as soon as OME accepts it, with `deadline exceeded in phase Accepted: surge never allocated`. This is a known bug, so keep a timeout set while you use migrations.
Keep your other values in the file: `helm upgrade` resets the values that aren't in the file to the chart's defaults. Upgrade with the chart version that you already run, which `helm list -n ome` shows in its `CHART` column:
```bash
helm upgrade --install ome oci://ghcr.io/moirai-internal/charts/ome-resources \
--namespace ome --version 1.3.0 -f values.yaml
```
Helm reports that the release has been upgraded, and the controller's pods restart with the new settings. Wait for the rollout:
```bash
kubectl rollout status deployment ome-controller-manager -n ome
```
```output
deployment "ome-controller-manager" successfully rolled out
```
Then read the settings back from the `inferenceservice-config` ConfigMap:
```bash
kubectl get configmap inferenceservice-config -n ome -o jsonpath='{.data.lifecycle}' \
| jq '{instanceReadyTimeout, stuckPodGracePeriod, unschedulableGracePeriod}'
```
```output
{
"instanceReadyTimeout": "30m",
"stuckPodGracePeriod": "2m",
"unschedulableGracePeriod": "15m"
}
```
A setting you turned off shows as `null`. A kustomize install sets only `instanceReadyTimeout`. To edit the ConfigMap directly instead, see [Change ConfigMap settings](../operate-ome/configure-the-controller.md#tune-the-config-cache).
## Troubleshooting
### The condition says no timeout is set
The `InstanceReadyTimeoutUnconfigured` condition is `True`, though you set a timeout. Check the places OME reads:
- In the InferenceService, the value goes under the component, `spec.engine.lifecycle`, not under `spec.lifecycle`.
- A `0s` in the InferenceService counts as unset, and hides the runtime's value.
- In the cluster settings, the value needs a unit and must be above zero. OME treats `"30"`, `0s` and `-5m` as unset.
To read the cluster settings, use the ConfigMap check in [Change the cluster defaults](#change-the-cluster-defaults).
### The Instance is stuck and never fails
An Instance stays in `Creating`, or another phase, past its deadline. Check its rows, as in [Step 2](#step-2-watch-the-instance-against-its-deadline):
| What you see | What to do |
| --- | --- |
| `operation deadline` is `-`, with an `op hold` row | Clear the wait it names: free quota or capacity, bring back the node, or resume the rollout. An unschedulable pod fails the attempt after `unschedulableGracePeriod`. |
| `operation deadline` is `-`, and `admitted` is `false` | A pod still has scheduling gates. The clock starts when Kueue, or whichever controller added them, removes them. |
| `operation deadline` is `-`, with no `op hold` row, and `admitted` is `true` | Check the condition, as in [Step 1](#step-1-give-a-component-more-time). If it names a timeout, the rollout is paused, which stops every Instance's clock until you [resume it](../roll-out-changes/pause-and-resume-a-rollout.md#step-3-resume-the-rollout). |
| `operation surge index` shows a number | The wait can be on the Instance at that index. Check its `op hold` and `admitted` rows. |
| All the Instance's pods serve traffic | Nothing: OME doesn't fail an Instance while all its pods serve. |
### The controller doesn't start after you change the settings
After a `helm upgrade`, the rollout doesn't finish, and the new controller pods crash-loop. Read the logs of every replica:
```bash
kubectl logs -n ome -l control-plane=ome-controller-manager --tail=-1
```
A pod that failed to start logs `Failed to initialize lifecycle scale configuration`, with an error that contains `unable to parse lifecycle config json`. The `lifecycle` entry isn't valid JSON, or a duration in it is a bare number. Give each duration a unit, and upgrade again. Until then, the replicas that were already running ignore the whole `lifecycle` entry, so only a component's own `instanceReadyTimeout` applies.
## Clean up
Delete the InferenceService and its namespace:
```bash
kubectl delete inferenceservice qwen3-0-6b -n qwen3-deadlines
kubectl delete namespace qwen3-deadlines
```
```output
inferenceservice.ome.io "qwen3-0-6b" deleted from qwen3-deadlines namespace
namespace "qwen3-deadlines" deleted
```
If you changed the chart values, put back the ones you want to keep in your values file, and upgrade the release again with the same `--version`.
## Next steps
- [Reset failed Instances](reset-failed-instances.md): start over an Instance that failed during a create or a restart.
- [Release a held revision](../roll-out-changes/release-a-held-revision.md): let OME try a revision again after it's held.
- [Instances](../../concepts/omenative/instances.md): the phases and operations of an Instance.
- [Troubleshoot an InferenceService](../troubleshoot/troubleshoot-an-inferenceservice.md): find why an InferenceService doesn't serve.