# SageMaker Transform Job Over-Provisioned

> A finished transform job cannot be resized, so the advice targets its next run. ZopNight flags jobs whose per-vCPU CPU averaged under 10% with memory under 50%, names the concrete one-tier-smaller ml.* instance, and prices the saving from the real BatchTransform rate delta between the two SKUs.

Source: https://zop.dev/integrations/aws/recommendations/sagemaker-transform-job-over-provisioned
Updated: 2026-08-19

---

## Advice for a job that no longer exists

Batch transform jobs are transient: they spin up, process, and terminate, billing
per-second while they run. By the time the finding appears, the flagged job is finished
and unfixable, so the recommendation is aimed at the recurring pattern, because transform
jobs are rerun on schedules and from pipelines, and next month's run can be a size
smaller. The cost basis reflects that: the job's actual measured run cost, not a
730-hour monthly projection that would inflate a two-hour job absurdly.

## Reading multi-core metrics correctly

[Transform-job CPU in CloudWatch](https://docs.aws.amazon.com/sagemaker/latest/dg/monitoring-cloudwatch.html) is summed across vCPUs: an 8-vCPU instance scales
0–800%. The rule divides by the instance type's published vCPU count before applying its
10% trigger, and abstains when the type's core count cannot be resolved. The peak guard
runs on the same normalized basis but deliberately skips the usual coverage minimum: a
transient job can never accumulate a week of peak-band coverage, so the run-spanning
recorded maximum is used directly. A possibly-understated peak can only withhold a
recommendation, never invent one.

## A named target priced from the billing rows AWS actually uses

"Use a smaller instance" is only actionable with a name, so the rule steps the ml.* type
one rung down the size ladder (abstaining for GPU SKUs, floor sizes, and unknown
families) and prices both SKUs from [the SageMaker offer's BatchTransform component rows](https://aws.amazon.com/sagemaker/pricing/),
which are the rates this workload actually bills under (several types have no bare-rate
row at all). Saving equals cost × (1 − target/current rate); missing rates or a
non-cheaper target abstain rather than fall back to a flat fraction.

## Inspect a job's utilization

```bash
aws cloudwatch get-metric-statistics \
  --namespace /aws/sagemaker/TransformJobs --metric-name CPUUtilization \
  --dimensions Name=Host,Value=my-transform-job \
  --start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --period 3600 --statistics Average Maximum
```

Divide by the vCPU count before judging.

## Applying it to the next run

Set the smaller InstanceType in the TransformResources block of the job definition your
pipeline submits, then consider whether InstanceCount or MaxConcurrentTransforms
also deserves a trim. One monitored rerun on the smaller size settles the question the
metrics raised.
