How to Build a GPU Cluster on AWS for LLM Training
I spent six months in 2024 trying to train a 13B parameter model on a single p4d.24xlarge. It took 47 days. The model was useless by the time it finished. We rebuilt the cluster three times before we got it right. What I’m about to share is what actually works.
So what does “build a GPU cluster on AWS for LLM training” mean in 2026? It means provisioning multiple GPU instances connected by high-speed networking, running a scheduler (Slurm or ParallelCluster), and using frameworks like PyTorch FSDP or DeepSpeed to split the model and data across GPUs. It’s not just buying big instances. It’s getting the networking right, the storage right, and the orchestration right.
You’ll learn:
- Which instances actually deliver for training (and which are a trap)
- Why EFA is non-negotiable
- How to avoid the checkpointing nightmare that killed our first model
- Cost hacks that save 40% without breaking training
- How to decide between AWS and GCP for distributed systems — spoiler: it’s not about price
Let’s get into it.
Why Not Just Rent a Single GPU?
Most people think you can train an LLM on a single A100. You can — if your model is under 7B parameters and you have two weeks to spare. Anything bigger, and you need a cluster. The math is brutal: a 70B model requires ~140GB of GPU memory just for parameters in FP16. No single GPU has that.
So you need model parallelism. And data parallelism. And sometimes pipeline parallelism. That’s what Distributed machine learning is about — splitting computation across devices while pretending it’s one big process.
I’ve seen teams burn $50K trying to train on a single instance with gradient checkpointing. It’s slower and more expensive than using 8 smaller instances connected right. Don’t do that.
Choosing the Right Instance Types for LLM Training
AWS offers a mess of GPU instances. Here’s the shortlist for training in 2026:
- p5.48xlarge (8x H100) — the gold standard. We use these for anything 30B+.
- p4d.24xlarge (8x A100 40GB) — still solid for smaller models. Cheaper.
- trn1.32xlarge (Trainium) — AWS’s custom chip. I was skeptical. Turns out it’s 40% cheaper than p5 for 70B training. The catch: the software stack is immature. Expect to debug.
- g5.48xlarge (4x A10G) — good for fine-tuning, not pretraining.
We benchmarked p5 vs trn1 on LLaMA 2 70B in Jan 2025. p5 was 1.2x faster but 1.7x more expensive. For production, we use trn1. For research, p5.
Rule of thumb: pick instances with 8 GPUs per node. More than that and EFA (Elastic Fabric Adapter) scaling degrades. Fewer and you waste cross-node bandwidth.
Networking: The Hidden Bottleneck
I can’t overstate this. If your cluster doesn’t have EFA, your GPUs will spend 60% of their time waiting for gradients. We proved this in a side-by-side test: p4d with EFA vs without — training throughput dropped 3.4x.
AWS says EFA provides “user-space access to the network adapter.” In plain English: it bypasses the kernel for GPU-to-GPU communication. That’s why Cloud-native and Distributed Systems for Efficient and ... emphasize that networking is the bottleneck in distributed training.
When I talk to teams evaluating aws vs gcp for distributed systems, I point to EFA. GCP has GPUDirect-TCPX, but AWS’s integration with Elastic Network Adapter is more mature for NCCL. Distributed training in Amazon SageMaker AI handles this automatically — you just specify the instance count and it provisions the EFA fabric.
But if you’re rolling your own cluster (like we did), you must:
- Launch instances in a placement group with “cluster” strategy.
- Enable EFA on the network interface.
- Use Amazon Linux 2 or Ubuntu 22.04 with the EFA installer.
Here’s a minimal CDK snippet for spinning up a 4-node p5 cluster with EFA:
typescript
const cluster = new ec2.CfnLaunchTemplate(this, 'P5Cluster', {
launchTemplateData: {
instanceType: 'p5.48xlarge',
placementGroupName: 'llm-cluster-pg',
networkInterfaces: [{
deviceIndex: 0,
interfaceType: 'efa',
groups: [securityGroup.ref],
}],
},
});
Don’t forget to install the EFA driver on first boot. We learned that the hard way — three days of zero throughput.
Storage Setup for Checkpointing and Data Loading
You need two storage tiers: fast scratch for data loading, and durable object storage for checkpoints.
For data loading, use FSx for Lustre. It delivers up to 1 TB/s throughput per file system. We saw 4.2x faster data loading compared to EBS gp3. The key: place Lustre in the same Availability Zone as your cluster.
For checkpoints, use S3. But write a script that flushes the Lustre checkpoint to S3 every N steps. Models can be 200GB+. If you write directly to S3, training stalls. Here’s our checkpoint helper:
python
import boto3
import subprocess
def save_checkpoint(local_path, s3_bucket, step):
s3 = boto3.client('s3')
# First save locally to Lustre (fast)
torch.save(model.state_dict(), local_path)
# Then async upload to S3
subprocess.Popen(['aws', 's3', 'cp', local_path, f's3://{s3_bucket}/checkpoint-{step}.pt'])
We also use S3 for dataset storage. Streaming directly from S3 with s3fs is okay for small datasets. For terabyte-scale, pre-download to Lustre.
Scheduling and Orchestration with Slurm or ParallelCluster
You can build a cluster manually. I did. It’s a nightmare. Use AWS ParallelCluster. It sets up Slurm, EFA, and FSx in about 20 minutes. We switched in Feb 2025 and never looked back.
ParallelCluster config is a YAML file. Here’s a simplified one for an LLM training cluster:
yaml
Region: us-east-1
HeadNode:
InstanceType: c6i.32xlarge
Networking:
SubnetId: subnet-xxx
Scheduling:
Scheduler: slurm
SlurmQueues:
- Name: gpu
ComputeResources:
- Name: p548
InstanceType: p5.48xlarge
MinCount: 4
MaxCount: 16
Efa:
Enabled: true
PlacementGroup:
Enabled: true
SlurmSettings:
EnableEfa: true
One gotcha: set MinCount to your expected cluster size. If you set it to 1 and let it scale dynamically, job scheduling time jumps to 5 minutes. For LLM training, you want static nodes during training.
Slurm jobs look like this:
bash
#!/bin/bash
#SBATCH --nodes=8
#SBATCH --ntasks-per-node=8
#SBATCH --gres=gpu:8
#SBATCH --exclusive
srun torchrun --nnodes=8 --nproc_per_node=8 train.py
ParallelCluster also integrates with SageMaker’s distributed training library. But we prefer raw Slurm for flexibility.
Distributed Training Frameworks: PyTorch DDP, FSDP, DeepSpeed
Don’t write your own gradient synchronization. Use existing frameworks.
For data parallelism: PyTorch DDP. Simple, works for models that fit in GPU memory. For model parallelism: FSDP (Fully Sharded Data Parallel). Shards parameters across GPUs. For 70B models, FSDP is the default.
DeepSpeed’s ZeRO-3 is similar but with more knobs. We tested both on 8 p5 nodes (64 H100s). FSDP was easier to debug. DeepSpeed ZeRO-3 was 7% faster but took two weeks to tune.
Here’s a code example using FSDP with a Hugging Face model:
python
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
from torch.distributed.fsdp.wrap import wrap
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-70b-hf")
model = FSDP(model, auto_wrap_policy=wrap) # wrap transformer layers
For mixed precision, use torch.cuda.amp. BF16 on H100s is stable.
One contrarian take: don’t use DeepSpeed’s activation checkpointing. We found it actually hurts throughput on H100s because it recomputes more than it saves. FSDP’s offload to CPU is more effective.
Monitoring and Cost Optimization
CloudWatch is okay but too slow for real-time GPU monitoring. Use NVIDIA DCGM + Prometheus + Grafana. We built a dashboard showing per-GPU utilization, memory bandwidth, and network throughput.
Cost hack: use Spot Instances for nodes that can be preempted. For training, that’s risky. But if you use PyTorch’s checkpointing to save every N steps, you can resume from last checkpoint. We saved 65% on our p5 cluster by mixing 70% spot with 30% on-demand. When spot was reclaimed, we reattached storage and launched a replacement.
Another tip: commit to Savings Plans for 1 or 3 years. We saved 52% on p5 instances. AWS charges more per hour than GCP, but with Savings Plans the gap narrows.
Common Pitfalls and How We Fixed Them
- Ethernet instead of EFA. We launched instances without EFA enabled. Training was 4x slower. Solution: terminate and recreate with correct launch template.
- Single EBS for checkpoints. EBS gp3 couldn’t handle 200GB checkpoints every 15 minutes. Switched to FSx for Lustre.
- NCCL timeout during all-reduce. This is a network congestion issue. Set
NCCL_IB_TIMEOUT=22andNCCL_IB_RETRY_CNT=7. We lost three days to this. - Training diverges at scale. Gradient accumulation steps need careful tuning. We increased batch size too fast. Use the Distributed Training & Large-Scale Systems guide for learning rate scaling.
- AI agent architecture patterns for scalability — not directly about clusters, but the same principle applies: your orchestration layer (Slurm) must handle failures gracefully. We now use a heartbeat mechanism that restarts jobs if a node dies.
FAQ
Q: How many GPUs do I need for a 7B model?
8 A100 80GB GPUs (1 node) will do fine for training. For fine-tuning, 4 is enough.
Q: Should I use AWS or GCP for distributed training?
It depends. AWS has better EFA and SageMaker integration. GCP has cheaper spot prices for A100s. For pure training, AWS wins on reliability. For inference, GCP’s TPU v5p is cheaper.
Q: Can I use ECS or Kubernetes instead of Slurm?
Yes, but we tested both. Kubernetes adds complexity (helm charts, network configs) for no gain in training throughput. Slurm is built for HPC. Keep it simple.
Q: What’s the cheapest way to learn on AWS?
Start with g5.xlarge (1 A10G) for small models. Cost ~$1.2/hour. Move to p4d when you hit memory walls.
Q: How do I handle checkpointing across nodes?
Use shared FSx filesystem. Each node saves to same Lustre path. PyTorch Distributed Checkpoint handles consistency.
Q: What about security?
Pin SG to allow only your jump host and inter-node traffic. Use VPC endpoints for S3. Encrypt EBS and FSx with KMS.
Q: How long does it take to build a cluster from scratch?
With ParallelCluster, 20 minutes. Manually, two days including debugging.
Conclusion
Building a GPU cluster on AWS for LLM training in 2026 is not just about throwing money at the biggest instances. It’s about networking, storage, and the right framework. We’ve gone through trial by fire — from a 47-day training run to finishing a 70B model in 5 days on a 16-node p5 cluster.
The key takeaways:
- Use EFA or don’t bother.
- Pick p5 for speed, trn1 for cost.
- FSx for Lustre for data and checkpoints. S3 for durability.
- Slurm via ParallelCluster. Don’t DIY.
- FSDP > DeepSpeed for most teams.
- Spot + Savings Plans cuts cost in half.
And remember: the cluster is just infrastructure. The real work happens in the training loop and the data pipeline. Get both right, and you’ll train models that matter.
Nishaant Dixit — Founder of SIVARO. Building data infrastructure and production AI systems since 2018. Built systems processing 200K events/sec.