Skip to main content
Every supported model ships as a python launch script under scripts/, and starting a training run is one command:
This page explains what that command does and how to change what it runs. For the meaning of individual training flags, see Argument Groups and the CLI Reference.

How a launch script starts a training job

A launch script is a recipe, not the training process. It assembles the full train.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.

Running a v1 launch script

Older Ray launch scripts can keep the free-function API by importing its compatibility module:
It retains execute_train, the command and checkpoint helpers, dataset download, Mooncake startup, and the original ExecuteTrainConfig. New scripts build a backend with config.create_backend(); the compatibility API does not support Kubernetes.

The structure of a launch script

The sections below walk scripts/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:
Single-recipe launchers skip the table and write their values directly in the flag blocks.

ScriptArgs — script options as CLI flags and MILES_SCRIPT_* env vars

The script’s own options are the fields of a ScriptArgs dataclass:
The @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

The execute() function builds the train.py command line as one f-string block per concern, then concatenates them:
Each block maps to a section of Argument Groups, which documents the flags themselves: One block has no Argument Groups section: 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:
  1. Append flags with --extra-args. The value is appended to the end of the train.py command line, and for a flag given twice the later occurrence wins — so this overrides any flag the recipe already sets:
  2. Set a script option, as a flag or an env var. Anything on ScriptArgs can come from the command line or from MILES_SCRIPT_*:
  3. Edit the script. The launcher is the canonical home of a recipe’s hyperparameters and is meant to be read and edited — change the _RECIPES values or the flag blocks directly for anything you want to keep.

What execute_train runs on your machine

The launcher hands the assembled flags to U.execute_train, which issues the EXEC: commands you see in the log, in order:
  1. Kills leftover sglang, miles, and redis processes and stops any previous Ray cluster.
  2. Starts a fresh cluster with ray start --head, using --num-gpus-per-node GPUs, and on some hosts the worker port flags described in Ports on each node.
  3. Runs the launcher’s before_ray_job_submit hook, if it has one (used for the ssh fan-out below).
  4. Builds the Ray runtime env for the job: PYTHONUNBUFFERED, CUDA_DEVICE_MAX_CONNECTIONS=1, NCCL_NVLS_ENABLE (your exported value if set, otherwise probed with nvidia-smi), MASTER_ADDR, no_proxy, and a PYTHONPATH containing the repo root and --megatron-path, plus anything from --extra-env-vars.
  5. Submits the job: ray job submit -- python3 train.py <architecture flags> <recipe flags>.
Two environment variables skip parts of this sequence: 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.

Ports on each node

Three kinds of ports share every node of a job:
  • Ephemeral ports. When a process opens an outgoing connection, the kernel gives it a source port from the host’s ephemeral port range, net.ipv4.ip_local_port_range (read it with cat /proc/sys/net/ipv4/ip_local_port_range; a stock Linux kernel uses 32768-60999). Any port in this range can be taken this way at any moment.
  • Ray worker ports. Each Ray worker process, which runs one actor such as a training rank or a rollout engine, serves Ray’s own traffic on a port from the range ray start gets through --min-worker-port and --max-worker-port, 10002-19999 by default.
  • Service ports. The ports Miles picks for what runs inside its actors: the SGLang engines, the router, and the training process group’s master port.
Ray worker ports and service ports are both picked first and bound later. A port picked inside the ephemeral range can be taken by an outgoing connection in between, and the bind then fails with “Address already in use”. Miles keeps the three apart:
  • It picks service ports only from a range outside both the ephemeral range and Ray’s 10002-19999, which each node works out from its own ephemeral range: 20000-32767 on a stock kernel, 1024-8999 where the ephemeral range is 9000-65000.
  • Where the ephemeral range overlaps 10002-19999, the ray start commands Miles runs add --min-worker-port 0 --max-worker-port 0. Ray then picks no port, and each worker binds a free port the kernel assigns at that moment, which no connection can take first.
If the ephemeral range covers every port from 1024 to 65535, as in some containers, nothing lies outside it: Miles logs a warning and picks service ports from 20000-65535, where the failure can still happen. Narrowing the ephemeral range on such hosts avoids it.

Multi-step and multi-node launchers

Two launcher shapes go beyond a single execute().

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):
The 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.py has worker (joins the head’s cluster and blocks) and train (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.py sshes every host of an MPI-style hostfile into the cluster (U.ssh_start_ray_workers as the before_ray_job_submit hook), 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 each megatron_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.
Architecture parameters are not self-validating. Two checkpoints from the same family can ship different --rotary-base, vocab padding, or normalization epsilon. Diff the checkpoint’s config.json against the file in scripts/models/ before a first run, and override any drifted value by appending it, e.g. --extra-args "--rotary-base 10000".

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 --*-path plug points for custom rollout, reward, and filter code.
  • Models — the per-model recipe pages built on these launchers.