Run DeepSeek-V3 Training with WorkloadRun
Run DeepSeek-V3 Training with WorkloadRun
This guide walks through running DeepSeek-V3 BF16 training on a GPU cluster with Cluster Readiness Engine (CRE), using Megatron-Bridge and WorkloadRun. Unlike the Nemotron 5 guide, which uses raw Megatron-LM, this example uses Megatron-Bridge’s recipe system and demonstrates the developer-friendly pattern of overriding the container’s scripts/performance/ with a cloned branch checkout — so developers can iterate on their own run_script.py / utils/overrides.py without rebuilding the image.
For an introduction to WorkloadRun and when to use it instead of a full Certification, see the WorkloadRun Quick Start. For generic WorkloadRun options (targeting nodes, measurements, framework types), see Run a WorkloadRun.
Prerequisites
- A Kubernetes cluster with GPU nodes (
nvidia.com/gpu.present=true) andkubectlaccess nvcrectlinstalled and the CRE controller running on the cluster — see Installation- An NGC API key for pulling the NeMo container image from
nvcr.io - The DeepSeek-V3 recipe fetches the model configuration from Hugging Face Hub at startup, so worker pods need outbound access to
huggingface.co(or a pre-populated Hugging Face cache mounted into the pods — see the env var comments below)
No dataset or checkpoint download is required: the recipe trains on synthetic data, and this guide runs a proxy-size model.
Megatron-LM vs Megatron-Bridge
Create the WorkloadRun manifest
Save the following as deepseek-v3-bf16.yaml:
How the branch override works
The pattern in this YAML is the developer-iteration pattern for Megatron-Bridge:
- Container ships everything heavy.
nvcr.io/nvidia/nemo:26.02.00already hasmegatron-bridge,megatron-core(with FSDP),transformers,flashinfer,DeepEP, and matching CUDA/NCCL — all version-pinned to work together. Nopip installat runtime. - Init container clones a branch of
https://github.com/NVIDIA-NeMo/Megatron-Bridgeinto a sharedemptyDirvolume. Onlyscripts/performance/matters here — it’s whererun_script.py,utils/overrides.py, and the recipe configs live. train.shprepends the clonedscripts/performance/toPYTHONPATHand invokes the clonedrun_script.pyby absolute path. Result:from utils.overrides import …resolves to the developer’s branch (override wins).from megatron.bridge import …andfrom megatron.core import …resolve to the container’s pre-installed packages (stable, version-matched).
- No image rebuild required. A developer commits to their branch on GitHub, points the init container’s
git clone -b ...at it, and re-submits the WorkloadRun. Iteration takes seconds (small clone) instead of minutes (image build/push).
Branch choice matters: pick a branch whose script + bridge API expectations match what the container ships. llmb-r0.2.0 works against nemo:26.02.00. A branch that depends on a newer bridge API will fail at import time — pick a different branch or use a newer NeMo image.
Key configuration
numNodes: 16— the number of nodes per job group, not a total. The orchestrator partitions all eligible GPU nodes into groups of this size and creates one job per group:numNodes: 16on a 16-node cluster creates a single 16-node job, whilenumNodes: 4on the same cluster would create four 4-node jobs that run in parallel. Set it to your full cluster size for a single full-scale run.-m deepseek -s v3— selects the DeepSeek-V3 architecture.-c bf16— BF16 precision (FP8 paths in this branch require a newer bridge thannemo:26.02.00ships).-g gb300— GPU-specific tuning hints. Also acceptsgb200,b200, orh100— match your cluster.-a dummy -p dummy --no-detach—run_script.pyin this branch was written as a SLURM job submitter;--no-detachmakes it run in-process undertorchrun. The-a/-pflags are required by argparse but unused when not actually submitting via SLURM.-ng / -gn—-ngisnumNodes × gpusPerNode;-gnmatchesgpusPerNode. ThePET_NNODESandPET_NPROC_PER_NODEenvironment variables are injected into worker pods automatically.-pp 1 -ep 64— pipeline parallelism off, 64-way expert parallelism. Works for the proxy-size model below; for the full model use-pp 4 -ep 64 -vp 4at 256+ GPUs. The productep × tp × ppmust divide the world size; with-pp 1and no tensor parallelism,-epcan equal the world size.model.hidden_size=1024 model.kv_channels=64— proxy model dimensions so the workload fits on a 64-GPU cluster. The full DeepSeek-V3 (671B) needs 256+ GPUs; drop these overrides when you have enough GPUs.model.pipeline_model_parallel_layout=null model.virtual_pipeline_model_parallel_size=null— clears the recipe’s layout/VPP defaults so they don’t conflict with the-pp 1we’re forcing.logger.log_throughput=true— emits TFLOPS per iteration into the training log; the built-inmegatron-bridgeLogProfile parses these for goodput.train.manual_gc=true train.manual_gc_interval=100— explicit garbage-collection pacing recommended for this recipe.resources— optional. When omitted, CRE auto-sets onlynvidia.com/gpu: <gpusPerNode>(both limits and requests); it does not guess memory or CPU. Set the block explicitly, as here, if you need CPU pinning or memory sizing.
CRE auto-handles NCCL environment variables, ComputeDomain and DRA setup, EFA/RoCE networking, and topology-aware orchestration based on the detected platform and GPU architecture.
Submit the WorkloadRun
The --workload-registry* flags create an nvcr.io image pull secret in the target namespace and inject it into the WorkloadRun automatically.
Monitor progress
Watch pods come up:
Lightweight status check:
Check goodput measurement in real time:
The training-loop log lines (in kubectl logs <pod> -c node -f) will look like:
…with a throughput per GPU (TFLOP/s/GPU): ... field appended on each iteration thanks to logger.log_throughput=true.
Generate a report
Example output (16 nodes × 4 GPUs, BF16 proxy model):
If a node fails, CRE records it in the WorkloadRun status with a reason (HardwareFailureDetected, ThresholdViolation, or WorkloadFailed); it never taints, cordons, or otherwise modifies the node.
To save as JSON:
Gang scheduling
If the cluster runs a gang-aware scheduler such as KAI Scheduler, add spec.gangScheduler so all pods in a job group are held until the entire gang can be placed:
The queue value is applied as the kai.scheduler/queue label on the pod template metadata. It must be a valid Kubernetes label value (at most 63 characters). See Run a WorkloadRun for details.
Clean up
Cancel the WorkloadRun (cascades to its Workflow, Jobs, and pods):
Changing precision
The -c flag in train.sh controls numerical precision. This example uses -c bf16. The llmb-r0.2.0 run_script.py also accepts bf16, fp8_cs, fp8_mx, fp8_sc, and nvfp4, but some FP8 variants require recipe code that’s only on newer (incompatible) branches against nemo:26.02.00 — stick with bf16 unless you’ve verified the branch supports your chosen FP8 mode. If you switch, also update metadata.name to keep the resource name aligned with what it’s actually doing.
Available models
The llmb-r0.2.0 run_script.py accepts model family + size via -m / -s:
Compatible models include DeepSeek-V3, GPT-OSS, LLaMA 3.1, and Qwen3. All use the same WorkloadRun pattern — swap -m deepseek -s v3 for the desired family/size, adjust parallelism (-pp, -ep, -vp) for your GPU count, and (optionally) drop the model.hidden_size=... model.kv_channels=... proxy overrides if you have enough GPUs for the full model.
Next steps
- How-to: Run a WorkloadRun — targeting nodes, measurements, framework options
- How-to: Run Nemotron 5 Training — the raw Megatron-LM equivalent of this guide
- CLI Reference: workloadrun
- API Reference: WorkloadRun