Skip to content

Beyond CloudWatch: GPU Observability for Training and Inference Workloads with NVIDIA DCGM Exporter

14 minute read
Content level: Advanced
1

This article explains why Amazon CloudWatch cannot natively monitor NVIDIA GPU utilization, memory, thermal, or error metrics on EC2-based training and inference workloads — and provides a practical implementation guide for deploying NVIDIA DCGM Exporter with Amazon Managed Prometheus and Managed Grafana to close that gap.


The problem: CloudWatch doesn't see inside GPUs

If you run GPU-accelerated training or inference on AWS — whether on Amazon EC2 P-series instances, AWS ParallelCluster with Slurm, Amazon EKS with GPU node groups, or SageMaker HyperPod — you've likely noticed that Amazon CloudWatch gives you no GPU-specific metrics by default. CloudWatch tells you the instance is running and how busy its CPU is. It does not tell you:

  • Whether the GPU's streaming multiprocessors are actually doing work
  • How much GPU memory is used vs. free
  • Whether thermal throttling is limiting performance
  • Which training job is consuming which GPU
  • Whether an ECC error is silently corrupting your gradients

For a $20–$100+/hr GPU fleet running training or inference at scale, this is a significant operational visibility gap. You're paying for GPU time but cannot verify it's being used effectively — or detect when it isn't.

This article explains what CloudWatch can and cannot see on GPU instances, introduces NVIDIA DCGM (Data Center GPU Manager) Exporter as the solution, describes the scenarios where it matters most, and provides a condensed implementation guide for deploying it on AWS.


What CloudWatch sees on a GPU instance

CloudWatch's standard EC2 metrics plus the optional CloudWatch Agent give you:

MetricSourceWhat it tells you
CPUUtilizationEC2 hypervisorCPU busy % (averaged across all vCPUs)
NetworkIn/OutEC2 hypervisorNetwork bytes transferred
EBSReadOps/WriteOpsEC2 hypervisorStorage I/O
mem_used_percentCloudWatch AgentRAM utilization (requires agent install)
disk_used_percentCloudWatch AgentDisk utilization (requires agent install)
StatusCheckFailedEC2 hypervisorInstance-level health (host or system failure)

What's notably absent: anything about the GPU — utilization, memory, temperature, power, errors, NVLink bandwidth, tensor core activity. The GPU is opaque to CloudWatch.

Why doesn't CloudWatch collect GPU metrics natively?
NVIDIA GPU metrics are exposed through NVIDIA's proprietary NVML (NVIDIA Management Library) and DCGM APIs — not through the standard Linux /proc or /sys interfaces that the CloudWatch Agent reads. Collecting GPU telemetry requires a GPU-aware agent that speaks NVML/DCGM. AWS provides this in managed contexts (SageMaker HyperPod has built-in DCGM integration), but on self-managed EC2, ParallelCluster, and EKS, you deploy it yourself.


When the gap matters — practical scenarios

These are real operational situations where the CloudWatch-only visibility gap causes problems:

Training workloads (HPC / Slurm / ParallelCluster)

#ScenarioWhat CloudWatch showsWhat you actually need
1Distributed training run is 3× slower than expectedCPU% is lowGPU SM utilization, EFA RDMA bandwidth, per-job FSx throughput — identify whether the bottleneck is compute, network, or storage
2Multi-node job failed at hour 4 of a 6-hour run — which node first, why?Slurm exit codeCorrelated XID/ECC errors, thermal throttle events, EFA link flaps — timestamped and cross-node
3User requested 4 GPUs for 12 hours but spent the first 6 downloading data at 0% GPU utilizationYou billed the same either wayGPU active-time vs. allocated-time reveals "allocated-but-idle" — silent waste
4Which team or user consumed $100K of GPU time last month?Total bill, no attributionPer-job GPU-hours via Slurm accounting + DCGM labels
5GPU training partition has 47 pending jobs — capacity exhaustion, fair-share issue, or one user monopolizing resources?Nothing — you SSH and run squeueReal-time queue depth per partition, per user, time-in-queue

Inference workloads (EKS / ECS / self-managed)

#ScenarioWhat CloudWatch showsWhat you actually need
6Model serving latency spiked — is it GPU saturation, memory pressure, or a framework bug?Pod CPU and memory (via Container Insights)GPU SM occupancy, tensor core utilization, FB memory used — distinguish "GPU is maxed" from "GPU is waiting on something else"
7One node in the inference fleet has degraded performance on random daysNothing on the GPUPer-device RDMA read/write, dropped packets, ECC errors — identifies a bad NIC or degrading GPU
8Auto-scaler isn't scaling fast enough — but is the existing fleet actually saturated?CPU-based scaling triggers don't applyCustom metrics: DCGM_FI_DEV_GPU_UTIL as a scaling signal — scale on actual GPU utilization, not proxied via CPU

The cross-cutting scenario (applies to both)

#ScenarioWhat CloudWatch showsWhat you actually need
9"The job stalled — was it the GPU, the auth system, the storage, or the scheduler?"Three separate consoles, manual correlationA single timeline where GPU activity, auth latency, storage throughput, and scheduler state are queryable together — the OpenTelemetry Collector layer that unifies DCGM + service metrics into one Prometheus/Grafana view

The solution: NVIDIA DCGM Exporter + Amazon Managed Prometheus + Amazon Managed Grafana

NVIDIA DCGM (Data Center GPU Manager) is the official, supported interface for collecting GPU telemetry from NVIDIA data-center GPUs (A100, H100, H200, B200, and all P-series instances on AWS). The DCGM Exporter is an open-source component that exposes these metrics in Prometheus format at an HTTP endpoint — making them scrapable by any Prometheus-compatible system.

The AWS-recommended architecture for GPU observability on EC2 and ParallelCluster:

GPU Compute Node(s)               Head Node / Monitoring
┌──────────────────────┐         ┌─────────────────────────────────┐
│ DCGM Exporter :9400  │──scrape─│ Prometheus (agent mode)          │
│ Node Exporter :9100  │──scrape─│ remote_write (SigV4)             │──── Amazon Managed
│ EFA Exporter  :9101  │──scrape─│                                  │     Service for Prometheus
│ Slurm Exporter:8080  │──scrape─│                                  │            │
└──────────────────────┘         └─────────────────────────────────┘            │
                                                                                ▼
                                                                    Amazon Managed Grafana
                                                                    ┌──────────────────────┐
                                                                    │ DCGM Dashboard 12239 │
                                                                    │ Jobs Dashboard       │
                                                                    │ EFA Dashboard        │
                                                                    │ Cluster Overview     │
                                                                    └──────────────────────┘

For EKS-based inference, the same pattern deploys as a Kubernetes DaemonSet:

kubectl / Helm
└── dcgm-exporter DaemonSet (nvidia-dcgm/dcgm-exporter chart)
    └── scraped by ADOT Collector or Prometheus Operator
        └── remote_write to AMP
            └── visualized in AMG

Why this pattern (and not self-hosted Prometheus/Grafana)?

ConsiderationSelf-hosted on head nodeManaged (AMP + AMG)
Scaling across clustersOne Prometheus per cluster; no unified viewOne AMP workspace spans all clusters
Operational burdenYou patch, scale, backup the TSDBAWS manages — zero ops
RetentionDisk-limited (weeks)150 days built-in, configurable
HA/DurabilitySingle-node SPOFMulti-AZ, replicated
CostEC2 time (hidden in head-node bill)~$0.90/10M samples + $0.03/GB stored; $9/user/mo for Grafana
Best forSingle small cluster, budget-constrainedMulti-cluster fleet, production GPU workloads at scale

For fleets larger than 1–2 clusters, the managed pattern is strongly preferred. For a single ParallelCluster in a dev/test context, self-hosted on the head node (per the AWS Prescriptive Guidance pattern) is a reasonable starting point.


Key DCGM metrics — what to focus on

DCGM exports ~35 metrics per GPU by default. Not all are equally useful. Here's what matters most for training and inference:

Utilization & efficiency ("Is the GPU actually working?")

MetricUnitWhat it tells you
DCGM_FI_PROF_SM_ACTIVE%True GPU utilization — fraction of time at least one warp is active on any SM. This is the "GPU utilization %" most teams want.
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE%Tensor core utilization — critical for H100/B200 training workloads. Low means the model isn't leveraging the hardware's core capability.
DCGM_FI_PROF_DRAM_ACTIVE%Memory-bandwidth utilization — high = memory-bound workload. Low SM + Low DRAM = idle GPU.
DCGM_FI_DEV_GPU_UTIL%Coarse busy% (nvidia-smi equivalent) — useful for dashboards but less precise than SM_ACTIVE.
DCGM_FI_DEV_MEM_COPY_UTIL%PCIe/DRAM copy-engine utilization — high = data-transfer bottleneck (host↔device).

Health & reliability ("Is something wrong?")

MetricUnitWhat it tells you
DCGM_FI_DEV_XID_ERRORSeventLast XID error code. XID 43, 63, 79 are especially bad — hardware/driver failures.
DCGM_FI_DEV_ECC_SBE_VOL_TOTALcountSingle-bit ECC errors — early warning of hardware degradation.
DCGM_FI_DEV_ECC_DBE_VOL_TOTALcountDouble-bit ECC errors — hardware failure; job likely corrupted.
DCGM_FI_DEV_GPU_TEMP°CDie temperature. Approaching thermal throttle? For sustained full-load training, this is the first metric to alarm on.
DCGM_FI_DEV_CLOCK_THROTTLE_REASONSbitmaskWhy is the GPU running slower? Thermal? Power cap? Software limit?

Memory ("Will I OOM?")

MetricUnitWhat it tells you
DCGM_FI_DEV_FB_USEDMiBFrame buffer (GPU memory) in use
DCGM_FI_DEV_FB_FREEMiBFrame buffer free — approaching 0 = imminent OOM crash

NVLink / interconnect ("Is communication the bottleneck?")

MetricUnitWhat it tells you
DCGM_FI_DEV_NVLINK_BANDWIDTH_TOTALcounterTotal NVLink bandwidth — for p5.48xlarge NVSwitch topology, low = model not using tensor parallelism effectively
DCGM_FI_PROF_NVLINK_TX_BYTES / RX_BYTESbytes/sPer-link throughput — identifies asymmetric or saturated links

Condensed implementation guide

Option A: Systemd on EC2 / ParallelCluster (training clusters)

Prerequisites:

  • NVIDIA driver + DCGM library present on the AMI (standard on AWS Deep Learning AMIs)
  • Amazon Managed Service for Prometheus (AMP) workspace created
  • Head-node IAM role with aps:RemoteWrite permission

Step 1 — Install DCGM Exporter on GPU compute nodes

Deploy via ParallelCluster's OnNodeConfigured hook or bake into the AMI:

#!/bin/bash
# Only install on GPU nodes
command -v nvidia-smi &>/dev/null || exit 0

DCGM_EXPORTER_VERSION="3.3.9-3.6.1"
curl -fsSLo /tmp/dcgm-exporter.tar.gz \
  "https://github.com/NVIDIA/dcgm-exporter/releases/download/${DCGM_EXPORTER_VERSION}/dcgm-exporter-${DCGM_EXPORTER_VERSION}-linux-amd64.tar.gz"
mkdir -p /usr/local/dcgm-exporter
tar -xzf /tmp/dcgm-exporter.tar.gz -C /usr/local/dcgm-exporter --strip-components=1

# Create systemd unit
cat > /etc/systemd/system/dcgm-exporter.service <<'EOF'
[Unit]
Description=NVIDIA DCGM Exporter
After=network-online.target
ConditionPathExists=/usr/bin/nvidia-smi
[Service]
ExecStart=/usr/local/dcgm-exporter/dcgm-exporter \
    --address=:9400 \
    --collectors=/usr/local/dcgm-exporter/etc/dcp-metrics-included.csv \
    --kubernetes=false
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now dcgm-exporter.service

Verify: curl -s http://localhost:9400/metrics | grep DCGM_FI_DEV_GPU_UTIL

Step 2 — Configure Prometheus agent on the head node

Run Prometheus in agent mode (scrape + forward, no local storage):

# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  external_labels:
    cluster: my-training-cluster

scrape_configs:
  - job_name: dcgm-exporter
    ec2_sd_configs:
      - region: us-west-2
        port: 9400
        filters:
          - name: tag:parallelcluster:node-type
            values: ["Compute"]
    relabel_configs:
      - source_labels: [__meta_ec2_instance_id]
        target_label: instance_id
      - source_labels: [__meta_ec2_instance_state]
        regex: "(pending|running)"
        action: keep

remote_write:
  - url: https://aps-workspaces.us-west-2.amazonaws.com/workspaces/<WORKSPACE_ID>/api/v1/remote_write
    sigv4:
      region: us-west-2
    queue_config:
      max_samples_per_send: 5000
      max_shards: 100

Start with --enable-feature=agent flag:

prometheus --config.file=/etc/prometheus/prometheus.yml --enable-feature=agent

Step 3 — Import the Grafana dashboard

In Amazon Managed Grafana: Dashboards → Import → grafana.com dashboard: 12239. This is the canonical NVIDIA DCGM Exporter dashboard — GPU overview, temperature, power, memory, ECC errors, NVLink, all pre-built.

Option B: DaemonSet on EKS (inference clusters)

helm repo add nvidia-dcgm https://nvidia.github.io/dcgm-exporter/helm-charts
helm repo update
helm install dcgm-exporter nvidia-dcgm/dcgm-exporter \
    --namespace monitoring --create-namespace \
    --set serviceMonitor.enabled=true

If using AWS Distro for OpenTelemetry (ADOT) as your collector, configure the Prometheus receiver to scrape the DCGM exporter service and remote-write to AMP. If using the Prometheus Operator, the ServiceMonitor created by the Helm chart handles discovery automatically.

Optional: Attribute GPU metrics to Slurm jobs

For HPC/training clusters, add a Slurm prolog script that writes the job ID to DCGM Exporter's job-mapping directory:

#!/bin/bash
# /etc/slurm/prolog.d/dcgm-jobmap.sh
JOBMAP_DIR=/var/run/dcgm-exporter/jobmap
mkdir -p "$JOBMAP_DIR"
for gpu in $(echo "$CUDA_VISIBLE_DEVICES" | tr ',' ' '); do
    echo "$SLURM_JOB_ID" > "$JOBMAP_DIR/$gpu"
done

Then add --hpc-jobmapping-dir=/var/run/dcgm-exporter/jobmap to the DCGM Exporter startup command. Every metric gains an hpc_job label — enabling per-job queries like:

avg by (hpc_job)(DCGM_FI_PROF_SM_ACTIVE{cluster="my-cluster"})

Sizing and cost

For a typical GPU fleet:

ComponentFormulaExample (20 GPU instances × 8 GPUs each)
AMP ingestion~35 metrics/GPU × 4 samples/min × 160 GPUs × 43,200 min/mo~$140/mo
AMP storage150-day retention included$0 additional
AMG$9/active user/mo$27–$90/mo (3–10 users)
DCGM Exporter (runtime)Runs on existing GPU nodes$0 incremental
Prometheus agent (head node)Runs on existing head node in agent mode$0 incremental
Total~$170–$230/mo for a 160-GPU fleet

Compare this to the fleet it monitors: 20 × p5.48xlarge at ~$100/hr on-demand = $72,000/mo (or ~$30,000/mo after Savings Plans). The observability cost is 0.3–0.8% of the fleet cost — trivial, and it pays for itself the first time you catch a silent GPU waste event or a hardware failure before it corrupts a multi-day training run.


When NOT to use DCGM Exporter

  • SageMaker Training Jobs / HyperPod: DCGM is already built in and configured. Don't install a separate exporter — use the native SageMaker observability stack. See Exported metrics reference.
  • Single-GPU dev instances (g4dn.xlarge, etc.): The overhead of the monitoring stack likely exceeds the value for a single developer box. Use nvidia-smi manually or the NVIDIA GPU monitoring CloudWatch integration available through certain AMIs.
  • Inference workloads on AWS Inferentia/Trainium chips: DCGM is NVIDIA-specific. For Inf2/Trn1 instances, use the Neuron Monitor and Neuron Prometheus plugin instead.

Well-Architected alignment

The AWS Well-Architected HPC Lens — Operate pillar explicitly calls out job-level observability as a best practice:

"Log job-level statistics, track anomalies and integrate your environment logging into a dashboard... If using AWS ParallelCluster with Slurm, leverage Slurm accounting with AWS ParallelCluster to log job-level statistics in an external database."

The pattern described in this article — DCGM Exporter + Slurm Exporter + AMP + AMG — is the reference implementation cited by the HPC Lens for this best practice.

For the broader Operational Excellence pillar of the standard Well-Architected Framework, this addresses:

  • OPS 5 (Implement telemetry): Application-level telemetry for GPU workloads
  • OPS 8 (Analyze workload health): Dashboard visibility into GPU fleet health, anomaly detection on ECC errors and thermal throttling
  • OPS 9 (Respond to events): Alarming on XID errors, double-bit ECC, and thermal limits enables event-driven response

Suggested alarm set (start conservative)

ConditionSeverityAction
DCGM_FI_DEV_XID_ERRORS > 0 (any XID event)CriticalPage on-call — potential hardware failure
DCGM_FI_DEV_ECC_DBE_VOL_TOTAL > 0 (double-bit ECC)CriticalPage + drain node — job data likely corrupted
DCGM_FI_DEV_GPU_TEMP > 85°C sustained 5 minWarningInvestigate cooling / workload distribution
DCGM_FI_DEV_CLOCK_THROTTLE_REASONS == thermal for > 10 minWarningNode may be underperforming; check airflow/placement
DCGM_FI_PROF_SM_ACTIVE < 20% for > 1 hr on a running job (requires hpc_job label)WarningSilent GPU waste — job is allocated but not computing

References


Licensing note

The DCGM Exporter itself is Apache 2.0. However, the Prometheus Slurm Exporter (cea-hpc/slurm_exporter) is GPLv3 — if your organization has commercial IP, run this through your OSS compliance review before adopting. The community slurm-job-exporter (guilbaults/slurm-job-exporter) is also GPLv3. An alternative is to use DCGM Exporter's built-in --hpc-jobmapping-dir with a simple prolog/epilog script (shown above) which avoids the GPLv3 dependency entirely.


Questions or feedback? Post in the comments. If you've deployed this pattern at scale and found additional metrics worth alarming on, share your experience — the community benefits from real-world operational learnings.