What is script mode and when would you build your own container instead?
Updated Aug 1, 2026
Short answer
Script mode supplies your training script to an AWS-managed framework container, so you write model code and AWS handles the image; a custom container is for unusual dependencies, non-supported frameworks or system-level requirements the managed image cannot satisfy.
Deep explanation
There is a ladder of increasing control and increasing maintenance burden:
- Built-in algorithm — no code.
- Script mode — your training script, AWS's image.
- Extend a managed image —
FROMan AWS DLC and add packages. - Bring your own container — fully custom image meeting the SageMaker contract.
Most teams should live on rung 2 and climb only when blocked. The managed Deep Learning Containers are non-trivially valuable: patched, optimised for AWS hardware, with correct CUDA/NCCL builds and EFA support already wired.
Script mode:
from sagemaker.pytorch import PyTorch
estimator = PyTorch( entry_point="train.py", source_dir="src", # whole dir uploaded; requirements.txt honoured role=role, framework_version="2.3", py_version="py311", instance_type="ml.g5.2xlarge", instance_count=1, hyperparameters={"epochs": 20, "lr": 3e-4},)The script reads SageMaker's environment contract:
# src/train.pyimport argparse, os, torch
p = argparse.ArgumentParser()p.add_argument("--epochs", type=int, default=10)p.add_argument("--lr", type=float, default=1e-3)# SageMaker injects these env vars — never hard-code pathsp.add_argument("--model-dir", default=os.environ["SM_MODEL_DIR"])p.add_argument("--train-dir", default=os.environ["SM_CHANNEL_TRAIN"])args = p.parse_args()
# ... training ...torch.save(model.state_dict(), os.path.join(args.model_dir, "model.pth"))Extending an image — the pragmatic middle ground:
FROM 763104351884.dkr.ecr.eu-west-1.amazonaws.com/pytorch-training:2.3-gpu-py311RUN apt-get update && apt-get install -y libgeos-dev # system dep pip can't provideCOPY requirements.txt /tmp/RUN pip install --no-cache-dir -r /tmp/requirements.txtFull BYOC must honour the contract: respond to train for training, run a server answering /ping and /invocations for inference, read from /opt/ml/input, write to /opt/ml/model.
Reach for BYOC when you need a framework AWS does not ship, a specific CUDA or compiler toolchain, licensed native libraries, or a bespoke serving stack. Accept that you now own base-image patching and CVE response.
Real-world example
A genomics team needed bioinformatics binaries with no pip equivalent. Rather than full BYOC, they extended the managed PyTorch DLC with an apt install and pinned Python packages — keeping AWS's CUDA and NCCL configuration while adding what they needed. Their Dockerfile is about fifteen lines, rebuilt weekly by CI against the latest DLC tag so security patches flow in automatically.
Common mistakes
- - Jumping to BYOC when script mode with a `requirements.txt` would have worked, and inheriting image maintenance forever.
- - Hard-coding `/opt/ml/...` paths instead of reading `SM_MODEL_DIR` and `SM_CHANNEL_*`, which breaks under different channel configurations.
- - Building a custom GPU image without matching CUDA, driver and framework versions — a slow, painful class of bug the DLCs exist to prevent.
- - Pinning to a `:latest` image tag, making builds unreproducible and pipeline caching useless.
- - Forgetting `/ping` must return 200 quickly, causing endpoint creation to fail health checks with an opaque error.
Follow-up questions
- How does SageMaker pass hyperparameters into your script?
- What is SageMaker local mode and why is it useful?
- How do you manage dependencies reproducibly in script mode?