One EKS cluster serves Qwen3-8B on both accelerators. Each pod owns its accelerator and runs vLLM; the Neuron device plugin exposes NeuronCores, and node scaling is handled by Karpenter / EKS Auto Mode (provisions the right instance per pod), not pod-level HPA.
βββββββββββββββββββββββββββββββ Amazon EKS control plane (managed) βββββββββββββββββββββββββββββββ β β β Ingress / ALB βββΆ vLLM Service βββΆ routes by model / node pool β β β β ββββββββββββββββββββ GPU node pool ββββββββββββββββββββ βββββββββββββ Trainium node pool βββββββββββββ β β β g6e.4xlarge (1Γ NVIDIA L40S, 48GB) β β trn2.3xlarge (1Γ Trainium2 chip, 4 cores) β β β β βββββββββββββββββββββββββββββββββββββ β β ββββββββββββββββββββββββββββββββββββββββββ β β β β β vLLM pod β β β β vLLM pod β β β β β β Qwen3-8B Β· CUDA Β· TP=1 β β β β Qwen3-8B Β· Neuron Β· TP=4 β 4 cores β β β β β β requests: nvidia.com/gpu: 1 β β β β requests: aws.amazon.com/neuron: 1 β β β β β βββββββββββββββββββββββββββββββββββββ β β ββββββββββββββββββββββββββββββββββββββββββ β β β β NVIDIA device plugin β β Neuron device plugin (DaemonSet) β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββββββ β β β β Karpenter / EKS Auto Mode β provisions g6e or trn2 nodes on demand to match pending pods β β Observability: neuron-monitor ββΆ CloudWatch Β· Neuron Explorer (offline profile viewer) β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
nvidia.com/gpu: 1aws.amazon.com/neuron: 1 (whole device)# Instance: g6e.4xlarge (NVIDIA L40S, 46GB) # Ubuntu 24.04 | CUDA 13.2 | vLLM 0.24 # Install pip install vllm # Serve vllm serve Qwen/Qwen3-8B \ --dtype bfloat16 \ --max-model-len 2048 \ --host 0.0.0.0 --port 8000 # Query (OpenAI-compatible API) curl http://localhost:8000/v1/chat/completions \ -d '{ "model": "Qwen/Qwen3-8B", "messages": [{"role":"user","content":"Hello"}], "max_tokens": 256 }'
# Instance: trn2.3xlarge (4 NeuronCores, 96GB) # Ubuntu 24.04 | Neuron SDK 2.25 | vLLM 0.16 + NxDI # Activate (pre-installed on Neuron DLAMI) source /opt/aws_neuronx_venv_pytorch_inference_vllm_0_16/bin/activate # Serve export VLLM_NEURON_FRAMEWORK=neuronx-distributed-inference vllm serve Qwen/Qwen3-8B \ --tensor-parallel-size 4 \ --max-model-len 2048 \ --host 0.0.0.0 --port 8000 # Query β IDENTICAL API! Same curl command works. curl http://localhost:8000/v1/chat/completions \ -d '{ "model": "Qwen/Qwen3-8B", "messages": [{"role":"user","content":"Hello"}], "max_tokens": 256 }'
With PyTorch Native, moving from GPU to Trainium is as simple as changing device='cuda' β device='neuron' and using torch.compile(backend="neuron"). No custom compilation steps, no model rewrites β same PyTorch code, different hardware.
import torch from transformers import AutoModelForCausalLM, AutoTokenizer # Load model model = AutoModelForCausalLM.from_pretrained( "Qwen/Qwen3-8B", torch_dtype=torch.bfloat16, ) # Move to device model = model.to("cuda") model.eval() # Compile for performance model.forward = torch.compile( model.forward, # uses default inductor backend ) # Tokenize tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B") inputs = tokenizer("Hello", return_tensors="pt").to("cuda") # Generate with torch.no_grad(): output = model.generate(**inputs, max_new_tokens=128) print(tokenizer.decode(output[0]))
import torch from transformers import AutoModelForCausalLM, AutoTokenizer # Load model model = AutoModelForCausalLM.from_pretrained( "Qwen/Qwen3-8B", torch_dtype=torch.bfloat16, ) # Move to device model = model.to("neuron") # β just change device model.eval() # Compile for Neuron model.forward = torch.compile( model.forward, backend="neuron", # β add backend ) # Tokenize tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B") inputs = tokenizer("Hello", return_tensors="pt").to("neuron") # β device # Generate β IDENTICAL call with torch.no_grad(): output = model.generate(**inputs, max_new_tokens=128) print(tokenizer.decode(output[0]))
Take a standard TorchTitan Qwen3-8B training run and move it to Trainium2 by
applying a 4-line diff β no custom kernels, no model rewrite.
Same torchrun β¦ torchtitan.train command, different device.
Commands and the TorchTitan.diff shown are verbatim from the AWS Neuron repo. Training
step metrics in this clip are illustrative for pacing, not a measured benchmark β see the
Throughput tab for real, server-side numbers.
# Neuron device plugin (DaemonSet) advertises NeuronCores # kubectl apply -f k8s-neuron-device-plugin.yml --- apiVersion: apps/v1 kind: Deployment metadata: name: vllm-qwen3 spec: replicas: 1 template: spec: nodeSelector: aws.amazon.com/neuron.present: "true" containers: - name: vllm image: public.ecr.aws/neuron/pytorch-inference-\ vllm-neuronx:0.16.0-sdk2.29.0 args: ["python", "-m", "vllm.entrypoints.openai.api_server", "--model", "/models/Qwen3-8B", "--served-model-name", "Qwen/Qwen3-8B", "--tensor-parallel-size", "4", "--max-model-len", "512", "--dtype", "bfloat16"] resources: limits: aws.amazon.com/neuron: 1 volumeMounts: - name: model mountPath: /models/Qwen3-8B volumes: - name: model hostPath: { path: /home/ubuntu/local-models/Qwen/Qwen3-8B }