scripts/, and starting a
training run is one command:
How a launch script starts a training job
A launch script is a recipe, not the training process. It assembles the fulltrain.py
command line for one model family, starts a local Ray cluster, and submits the command
as a Ray job. The pieces involved:
When the script starts, it prints its resolved options as a table, then every shell
command it issues with an
EXEC: prefix — the log is a complete record of what ran.
The structure of a launch script
The sections below walkscripts/run_qwen3_dense.py; every launcher follows the same
layout. The module docstring states the prerequisites (a converted checkpoint, the
datasets) and a runnable example, then three parts follow in file order.
Selecting a recipe with --model-name
For some models one launcher provides multiple recipes, selected with --model-name.
The per-variant values live in a _RECIPES table at the top of the file:
ScriptArgs — script options as CLI flags and MILES_SCRIPT_* env vars
The script’s own options are the fields of a ScriptArgs dataclass:
@U.dataclass_cli decorator exposes each field twice: as a --kebab-case command
line option (--model-dir) and as an environment variable with the MILES_SCRIPT_
prefix (MILES_SCRIPT_MODEL_DIR). A value given on the command line beats the
environment variable, which beats the field default. The env form is how launch
wrappers and cluster tooling inject machine-specific values without editing the script.
Options shared by every launcher, from the ExecuteTrainConfig base class and repo
convention:
execute() — assembling the train.py flags from grouped blocks
Theexecute() function builds the train.py command line as one f-string block per
concern, then concatenates them:
Two blocks have no Argument Groups section:
misc_args carries the cluster shape
(--colocate, --actor-num-nodes, --actor-num-gpus-per-node), and the wandb flags
come from U.get_default_wandb_args, which returns them only when WANDB_API_KEY is
set — so wandb logging turns on by exporting the key, with no script change.
Three ways to override a recipe
From lightest to heaviest:-
Append flags with
--extra-args. The value is appended to the end of thetrain.pycommand line, and for a flag given twice the later occurrence wins — so this overrides any flag the recipe already sets: -
Set a script option, as a flag or an env var. Anything on
ScriptArgscan come from the command line or fromMILES_SCRIPT_*: -
Edit the script. The launcher is the canonical home of a recipe’s
hyperparameters and is meant to be read and edited — change the
_RECIPESvalues or the flag blocks directly for anything you want to keep.
What execute_train runs on your machine
The launcher hands the assembled flags toU.execute_train, which issues the EXEC:
commands you see in the log, in order:
- Kills leftover
sglang,miles, andredisprocesses and stops any previous Ray cluster. - Starts a fresh cluster with
ray start --head, using--num-gpus-per-nodeGPUs. - Runs the launcher’s
before_ray_job_submithook, if it has one (used for the ssh fan-out below). - Builds the Ray runtime env for the job:
PYTHONUNBUFFERED,CUDA_DEVICE_MAX_CONNECTIONS=1,NCCL_NVLS_ENABLE(your exported value if set, otherwise probed withnvidia-smi),MASTER_ADDR,no_proxy, and aPYTHONPATHcontaining the repo root and--megatron-path, plus anything from--extra-env-vars. - Submits the job:
ray job submit -- python3 train.py <architecture flags> <recipe flags>.
The head-node address defaults to
127.0.0.1 and is taken from MASTER_ADDR; export
it on multi-node runs so Ray and torch distributed bind to the right interface.
Multi-step and multi-node launchers
Two launcher shapes go beyond a singleexecute().
Subcommand pipelines: prepare-* and full-train
Large-model launchers split the pipeline — download, precision cast,torch_dist
conversion, training — into subcommands of one script (underscores in the function name
become dashes on the CLI):
full-train subcommand chains all steps and checks a sentinel file before each
one, so a completed step is skipped on re-run — after an interruption, relaunch the
same command and it resumes where it stopped.
Joining multiple nodes: per-role subcommands and ssh fan-out
A multi-node run needs every node in the Ray cluster before the job is submitted. Launchers express this in one of two ways:- One subcommand per node role.
scripts/run_nemotron_3_super_120b_a12b.pyhasworker(joins the head’s cluster and blocks) andtrain(starts the head, waits until the cluster reports every GPU, then submits); you run one command on each node. - ssh fan-out from the head. With
--join-ray-workers,scripts/run_qwen3_sft.pysshes every host of an MPI-style hostfile into the cluster (U.ssh_start_ray_workersas thebefore_ray_job_submithook), so the whole cluster comes up from a single command on the head node.
Model architecture definitions in scripts/models/
Megatron cannot read the architecture from a HuggingFace checkpoint, so eachmegatron_model_type has a file in scripts/models/ named exactly after it, exposing
one function that returns the architecture flags:
execute_train resolves the file by name and splices its output into the train.py
command line ahead of the recipe flags. A variant (a layer-pruned debug model, a LoRA
target) derives from its base file with load_sibling_model_args instead of copying
it.
Next
- Argument Groups — which training flags belong to which block, and what they mean.
- CLI Reference — every flag Miles accepts.
- Quick Start — downloading and converting a checkpoint, the step before any launcher.
- Customization — the
--*-pathplug points for custom rollout, reward, and filter code. - Models — the per-model recipe pages built on these launchers.

