Skip to main content
modded-nanogpt is a community speedrun: train a GPT-2-sized language model to a validation loss of 3.28 on FineWeb as fast as possible on 8 NVIDIA H100s. The 2024 baseline took 45 minutes. The current record trains in under a minute. In this example you rent an H100 SUNK cluster on Andromeda and run the current record on one of its nodes. SUNK is Slurm running on the cluster’s Kubernetes: you get a login node over SSH, Slurm with Pyxis for containers, and a Ceph filesystem shared by every node. The code and data live in your home directory on that filesystem, the training runs in a container, and the job is submitted with sbatch so it keeps running when your SSH session ends.

What you need

  • An Andromeda account in an organization, with permission to place orders and the clusters.read and clusters.ssh permissions. See Permissions.
  • The andromeda CLI, jq, and OpenSSH, set up as in the quickstart.
1

Rent a SUNK cluster

In the console, open On demand and pick an H100 offer. On Configure on-demand capacity:H200 offers work too. The speedrun’s attention kernel is built for Hopper, so other GPU types do not run it. On-demand clusters are billed until you end them; see When you’re done.
2

Wait until Slurm and storage are ready

Find the new cluster and save its ID in CLUSTER_ID, as in the quickstart. The cluster shows provisioning, then active:
active means the nodes are assigned. Slurm and Ceph come up after that. Check both:
3

Connect and check the nodes

Register your SSH key and add the training host alias from Connect over SSH. Use your own cluster ID and username in the alias.
Your first key restarts the cluster’s compute nodes, and job steps fail on a node until it has restarted. On a new cluster nothing is running yet, so register the key now, before you submit the job.
List the partitions with their GPUs and node features:
compute-h100 is the partition for the cluster’s H100 nodes, and the job uses it. On an H200 cluster it is compute-h200. all is the default partition, with every node. cu128 means the nodes provide CUDA 12.8.
4

Get the code

Clone the repository into your home directory on the cluster:
/home/<username> is on the cluster’s Ceph filesystem, so the login node slurm-login-0 and the compute node that runs the job see the same checkout.

The batch script

Resources

The job takes one whole H100 node: all 8 GPUs, every CPU (--exclusive), and all memory (--mem=0). The speedrun itself is short, but the first run also builds a container and compiles the model, so the time limit leaves room for that. The log goes to nanogpt-speedrun-<job ID>.out in the directory you submit from. On an H200 cluster, set --partition=compute-h200.

The container

The speedrun pins PyTorch 2.10 and an attention kernel that needs the CUDA 13 runtime. SUNK compute nodes run CUDA 12.8 on NVIDIA driver 580, which also runs CUDA 13 programs. So the job brings CUDA 13 in a container, and the node’s driver runs it. SUNK compute nodes have Pyxis and enroot installed, so srun --container-image works without setup. The image comes straight from Docker Hub. Inside the container your home directory is mounted and you are root, so apt-get works. --container-save writes the finished container to ~/nanogpt/nanogpt.sqsh on the Ceph filesystem, where every node sees it. Later runs start from that file and skip this step. The build runs inside the job, so on the first run the whole 8-GPU node is held, with its GPUs idle, while packages install. Delete the file to rebuild it, for example after the repository changes its requirements.

The data

This downloads the FineWeb validation set and the first 9 training chunks, 900M tokens of pre-tokenized text, from Hugging Face into modded-nanogpt/data/fineweb10B. Later runs skip files already downloaded.

Training

torchrun starts one training process per GPU. Slurm sees one task, and SUNK confines each task to the CPUs Slurm gives it, so --cpus-per-task gives that task every CPU on the node. Without it, all eight processes would share one CPU. The ranks talk to each other over NVLink inside the node. On clusters with a RoCE network, SUNK sets NCCL_NET=IB for multi-node jobs. unset NCCL_NET keeps NCCL from looking for that network on a one-node run. On InfiniBand clusters the variable is not set, and unsetting it changes nothing.

The full script

Save this as speedrun.sbatch on your machine:
speedrun.sbatch

Run it

Copy the script to the cluster and submit it:
The job now runs on a compute node, independent of your SSH session. Check its state with ssh training 'squeue --me'. PD means it is waiting for a free node, and R means it is running.

Watch it

The SSH gateway can close this session at any time, for example when someone changes the cluster’s configuration. That does not affect the job. Run the command again to keep watching. On the first run the log starts with the container build and the data download. Then the model compiles:
After that, training logs its progress. The last validation line reports the result:
The run reaches the target when val_loss is at most 3.28. train_time counts only the timed training loop. Records are timed on H100s, so expect a different time on H200s. The job’s total time is longer: the first run adds the container build, the download, and about seven minutes of compilation. The trainer also writes a full log, including the source code it ran, under modded-nanogpt/logs/.

When you’re done

On-demand clusters are billed until they end. To stop paying, open the cluster in the console and choose End cluster. Copy anything you want to keep out of ~/nanogpt first, for example with scp; see Transfer files.

Troubleshooting

Next

  • Run it again. With the container and data in place, the job goes straight to compiling and training.
  • Read train_gpt.py and the track_1_short/ package. The repository README explains what each record changed.
  • Configure Slurm, for example to add a prolog that checks GPU health before every job.