Skip to content

EC2 Deployment

The Ray Train DLC is a training image. Unlike a one-shot torchrun launch, Ray needs a cluster: you start one container as the head and the rest as workers pointing at it, then submit jobs to the head.

Single-GPU Training

On one instance Ray needs no cluster setup — ray.init() inside the script starts a local Ray instance:

docker run --rm -it --gpus all --shm-size=16g --ipc=host \
  -v $(pwd):/workspace \
  public.ecr.aws/deep-learning-containers/ray:train-ml-cuda \
  python3 train.py

--shm-size=16g --ipc=host is required — Ray's object store and PyTorch's DataLoader workers both share memory through /dev/shm.

Multi-GPU Training (single node)

The container command is unchanged. Ray spreads workers over the GPUs it can see, so scale by raising num_workers in the script rather than by wrapping the launch in torchrun:

from ray.train import ScalingConfig
from ray.train.torch import TorchTrainer

trainer = TorchTrainer(
    train_func,
    scaling_config=ScalingConfig(num_workers=8, use_gpu=True),
)

Ray Train handles process placement and the NCCL process group; ray.train.torch.prepare_model() wraps your model in DDP.

Multi-Node Training (EFA)

For multi-node training on EFA-capable instances (e.g., p5.48xlarge, p4d.24xlarge), the image ships EFA + the NCCL OFI plugin so collectives flow over EFA automatically.

Run the containers with --privileged (or grant the EFA capabilities via --device) and use --network host so the workers can reach the head's GCS. Start the head on one instance:

docker run -d --name ray-head --gpus all --privileged --network host \
  --shm-size=16g --ipc=host -v /shared:/shared \
  public.ecr.aws/deep-learning-containers/ray:train-ml-cuda \
  ray start --head --port=6379 --dashboard-host=0.0.0.0 --block

Then join each worker to it:

docker run -d --name ray-worker --gpus all --privileged --network host \
  --shm-size=16g --ipc=host -v /shared:/shared \
  public.ecr.aws/deep-learning-containers/ray:train-ml-cuda \
  ray start --address=<head_private_ip>:6379 --block

--block keeps ray start in the foreground so the container's lifetime matches the Ray node's. Confirm the cluster formed, then submit a job:

docker exec ray-head ray status
docker exec ray-head ray job submit --address http://localhost:8265 --working-dir /shared/code -- python3 train.py

Checkpoints and datasets must live on storage every node can reach — an NFS or FSx mount (/shared above) or an s3:// path in RunConfig(storage_path=...).

Verify EFA Connectivity Before Training

The image includes the NCCL all_reduce_perf binary at /usr/local/bin/all_reduce_perf. Run it across nodes to confirm EFA + NCCL plumbing before spending GPU-hours on a real job:

docker exec ray-head mpirun -np 16 -N 8 -hostfile /shared/hosts.txt \
  -x NCCL_DEBUG=INFO -x FI_PROVIDER=efa \
  /usr/local/bin/all_reduce_perf -b 8 -e 1G -f 2 -g 1

NET/OFI Selected provider is efa in the output confirms EFA is carrying the collectives.

SSH Between Nodes

Multi-node MPI launches require SSH between containers. The image ships a pre-configured OpenSSH server. Start it with /usr/sbin/sshd, run it on a spare port such as 2022 (--network host leaves port 22 to the host), and add your public key to /root/.ssh/authorized_keys. The default configuration permits root login, so restrict access to your cluster's security group and supply your own keys and hardening.

Building on the Image

The image includes gcc, gcc-c++, make, cuda-nvcc, and cuda-cudart-devel, so you can build CUDA extensions in-place. PyTorch headers and libraries are visible at /opt/venv/lib/python3.13/site-packages/torch/.

Troubleshooting EFA Throughput

If all_reduce_perf runs but throughput is much lower than expected, check that FI_PROVIDER=efa is exported (otherwise NCCL falls back to sockets), and that the NIC is mounted in the container. lspci | grep -i mellanox inside the container should list the EFA adapter when EFA is plumbed correctly.