, ,

How to read GPU logs in Kubernetes

Metrics tell you a GPU failed; logs tell you why. Where GPU logs live on a Kubernetes node, how to ship them with Fluent Bit to Loki, and the six log lines that explain most GPU failures, with a live demo.

How to read GPU logs in Kubernetes: a log panel with an Xid 79 kernel line highlighted

AI infrastructure · Kubernetes · Fluent Bit · Loki · Observability

In How to monitor GPUs in Kubernetes I covered metrics: the numbers that tell you a GPU is hot, idle or broken. Metrics tell you that something went wrong. Logs tell you why. This post covers where GPU logs live on a Kubernetes node, how to collect them, and the six log lines that explain most GPU failures. There is a live demo where you can break a cluster and find each line yourself.

Try it: GPU Log Watch

A simulated GPU cluster writing real-looking logs. Run LogQL queries, trigger an Xid error, an out-of-memory crash or an NCCL timeout, and find the line that explains it. Nothing to install.

Open the demo →

Why logsWhat a metric shows, and what the log says

Here are the same four failures from the monitoring post. On the left is what your dashboard shows. On the right is the log line that tells you what to do.

FailureThe metric showsThe log says
GPU drops out
DCGM_FI_DEV_XID_ERRORS = 79, then the GPU stops reporting
NVRM: Xid (PCI:0000:3b:00): 79, … GPU has fallen off the bus.
Out of memory
GPU memory near 100%, then the pod restarts
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 GiB. GPU 1 … of which 1.07 GiB is free.
Job hangs
Utilisation drops to 0% on every GPU in the job, for ten minutes
Watchdog caught collective operation timeout: WorkNCCL(SeqNum=48211, OpType=ALLREDUCE …)
Node goes dark
The DCGM exporter target is down. No GPU metrics from that node
Failed to initialize NVML: Driver/library version mismatch

The metric tells you where to look. The log tells you whether it is your code, your configuration or the hardware, and that decides who gets the ticket.

The sourcesWhere logs live on a GPU node

The common mistake is to think kubectl logs shows everything. It only shows what a container wrote to its output, and it disappears when the pod is deleted. The most important GPU errors never reach a container at all.

Node · kernel Kernel ring buffer

The NVIDIA driver writes here. Xid errors, PCIe errors, NVLink faults and driver version problems all show up here first.

Not in kubectl logs dmesg · journalctl -k
Pods · stdout and stderr Container output

Your training script, vLLM, and the GPU Operator’s own pods: the device plugin and the DCGM exporter. CUDA, NCCL and PyTorch errors land here.

In kubectl logs, until the pod is gone /var/log/containers/*.log
Node · services kubelet and containerd

Why a container restarted, failed its health check or is in CrashLoopBackOff. Also image pulls and volume mounts.

Not in kubectl logs journalctl -u kubelet
Cluster · API Kubernetes events

Scheduling decisions such as Insufficient nvidia.com/gpu, evictions and OOM kills. Kept for one hour by default.

kubectl get events, for one hour kube-apiserver
The rule. Collect logs off the node before you need them. When a GPU falls off the bus, the node usually gets rebooted, and the kernel buffer is cleared. If the lines were not shipped somewhere, they are gone.

The pipelineHow the logs leave the node

The common open-source setup is Fluent Bit on every node, sending to Loki, with Grafana for searching. Fluent Bit reads the files and the system journal, adds Kubernetes labels, and ships the lines. Loki stores them cheaply because it only indexes the labels, not the text.

EACH GPU NODE KernelXid, PCIe, NVLink ContainersCUDA, NCCL, vLLM kubeletrestarts, probes Fluent Bit DaemonSet adds node, pod, namespace labels Loki index: labels only lines: compressed in object storage push Grafanasearch with LogQL Loki rulerlog-based alerts Alertmanager

Fluent Bit runs once per node, like the DCGM exporter. Grafana Alloy is the other common choice for the node agent. Promtail, which many older guides use, has been deprecated in favour of Alloy.

Labels or content: the one decision that matters in Loki

Loki groups lines into streams, one per unique set of labels. A few hundred streams is fast and cheap. Millions of streams make Loki slow and expensive. So keep labels to things with a small, fixed set of values, and leave everything else in the line, where you can still search it.

Make it a label

Small number of values. You filter by it in almost every query.

nodenamespacepodcontainersource
Keep it in the line

A new value for every request or step. Search it with a filter like |= "cmpl-8f2a".

request IDtrace IDGPU UUIDstep numberuser

The linesSix log lines worth knowing by heart

Each of these is a scenario in the demo. The red part is what you search for.

The GPU has goneKernel
NVRM: Xid (PCI:0000:3b:00): 79, pid='<unknown>’, name=<unknown>, GPU has fallen off the bus.
What it meansThe card stopped answering on the PCIe bus. Hardware, not your code.
What to doCordon and drain the node, reboot it. If it comes back, open a hardware ticket.
The device plugin gives up on a GPUGPU Operator
XidCriticalError: Xid=79 on Device=GPU-4f1c9a2e-…; marking device as unhealthy.
What it meansKubernetes removes the GPU from the node’s allocatable count. Jobs that need all GPUs on that node no longer fit.
What to doSame as above. This line is the bridge between the kernel error and the scheduling problem.
The job asked for too much memoryContainer
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 GiB. GPU 1 has a total capacity of 79.19 GiB of which 1.07 GiB is free.
What it meansYour code, not the hardware. The numbers tell you how far over you are.
What to doLower the batch size, or turn on gradient checkpointing or mixed precision.
One rank stopped talkingContainer
[Rank 0] Watchdog caught collective operation timeout: WorkNCCL(SeqNum=48211, OpType=ALLREDUCE, … Timeout(ms)=600000) ran for 600043 milliseconds before timing out.
What it meansAll GPUs wait for each other at every step. One did not arrive for 10 minutes, so the job killed itself.
What to doThe rank that did not log this line is your suspect. Check its node’s kernel log and metrics just before the timeout.
vLLM is out of roomContainer
WARNING … Sequence group cmpl-8f2a… is preempted by PreemptionMode.RECOMPUTE mode because there is not enough KV cache space.
What it meansToo many requests at once for the GPU memory left for the KV cache. Requests are paused and recomputed later, so responses get slower.
What to doAdd replicas, or lower the maximum number of requests each replica runs at once.
The driver was upgraded, the node was not rebootedGPU Operator
Failed to initialize NVML: Driver/library version mismatch
What it meansNew driver files on disk, old kernel module still loaded. Every GPU tool on the node fails, including the device plugin and the DCGM exporter.
What to doReboot the node. Next time, drain and reboot as part of the driver upgrade.
Python tracebacks span many lines. By default each line becomes a separate log entry, and the error ends up separated from where it happened. Turn on Fluent Bit’s python multiline parser so a traceback arrives as one entry.

Five LogQL queries to start with

LogQL is Loki’s query language. A query is a label selector in braces, then optional filters. Paste any of these into the demo.

# Every Xid error on any node
{source="kernel"} |= "NVRM: Xid"

# Errors from one training job, ignoring progress lines
{namespace="ml-train"} |~ "Error|out of memory|timeout"

# vLLM without the access log noise
{namespace="serving"} != "POST /v1"

# Anything wrong with the GPU Operator itself
{namespace="gpu-operator"} |~ "Failed|Error|unhealthy"

# Count Xid lines per node over 5 minutes (this one can become an alert)
sum by (node) (count_over_time({source="kernel"} |= "NVRM: Xid" [5m]))

The setupBuild it on your own cluster

This is a lab starting point: one Loki instance on local disk and Fluent Bit on every node. For production, Loki should store data in object storage such as S3. Check the values against the chart versions you install, as both charts change often.

  1. Install Loki in single-binary mode

    Save this as loki-values.yaml. It runs one Loki pod with local storage and turns off the caches you do not need in a lab.

    deploymentMode: SingleBinary
    loki:
      auth_enabled: false
      commonConfig:
        replication_factor: 1
      storage:
        type: filesystem
      schemaConfig:
        configs:
        - from: "2024-01-01"
          store: tsdb
          object_store: filesystem
          schema: v13
          index:
            prefix: loki_index_
            period: 24h
    singleBinary:
      replicas: 1
    read:
      replicas: 0
    write:
      replicas: 0
    backend:
      replicas: 0
    chunksCache:
      enabled: false
    resultsCache:
      enabled: false
    helm repo add grafana https://grafana.github.io/helm-charts
    helm repo update
    helm install loki grafana/loki -n logging --create-namespace -f loki-values.yaml
  2. Install Fluent Bit with GPU-friendly inputs

    Container logs come from files. Kernel and kubelet logs come from the system journal. The kubernetes filter adds the pod labels.

    env:
      - name: NODE_NAME
        valueFrom:
          fieldRef:
            fieldPath: spec.nodeName
    config:
      inputs: |
        [INPUT]
            Name              tail
            Tag               kube.*
            Path              /var/log/containers/*.log
            multiline.parser  cri
            Mem_Buf_Limit     50MB
        [INPUT]
            Name              systemd
            Tag               host.kernel
            Systemd_Filter    _TRANSPORT=kernel
            Read_From_Tail    On
        [INPUT]
            Name              systemd
            Tag               host.kubelet
            Systemd_Filter    _SYSTEMD_UNIT=kubelet.service
            Read_From_Tail    On
      filters: |
        [FILTER]
            Name                  kubernetes
            Match                 kube.*
            Merge_Log             On
        [FILTER]
            Name                  multiline
            Match                 kube.*
            multiline.key_content log
            multiline.parser      python
      outputs: |
        [OUTPUT]
            Name    loki
            Match   kube.*
            Host    loki-gateway.logging.svc.cluster.local
            Port    80
            Labels  source=container, node=${NODE_NAME}, namespace=$kubernetes['namespace_name'], pod=$kubernetes['pod_name'], container=$kubernetes['container_name']
        [OUTPUT]
            Name    loki
            Match   host.kernel
            Host    loki-gateway.logging.svc.cluster.local
            Port    80
            Labels  source=kernel, node=${NODE_NAME}
        [OUTPUT]
            Name    loki
            Match   host.kubelet
            Host    loki-gateway.logging.svc.cluster.local
            Port    80
            Labels  source=kubelet, node=${NODE_NAME}
    helm repo add fluent https://fluent.github.io/helm-charts
    helm install fluent-bit fluent/fluent-bit -n logging -f fluent-bit-values.yaml

    Journal records arrive as JSON with the text in a MESSAGE field. In Grafana, add | json | line_format "{{.MESSAGE}}" to a query to show just the text.

  3. Add Loki to Grafana

    In Grafana, go to Connections → Data sources → Add → Loki and use http://loki-gateway.logging.svc.cluster.local. Open Explore, pick Loki and run {source="kernel"}. You should see each node’s kernel messages.

  4. Turn the important lines into alerts

    The easiest route is a Grafana alert rule with Loki as the data source: Alerting → Alert rules → New, paste the query, set the threshold to above 0. These are the five rules the demo uses:

    # critical: hardware
    sum by (node) (count_over_time({source="kernel"} |= "NVRM: Xid" [5m])) > 0
    # critical: job killed
    sum by (pod) (count_over_time({namespace="ml-train"} |= "Watchdog caught collective operation timeout" [10m])) > 0
    # critical: node has no working GPU tools
    sum by (node) (count_over_time({namespace="gpu-operator"} |= "Failed to initialize NVML" [5m])) > 0
    # warning: job crashed on memory
    sum by (pod) (count_over_time({namespace="ml-train"} |= "CUDA out of memory" [10m])) > 0
    # warning: inference is slowing down
    sum(rate({namespace="serving"} |= "is preempted by" [1m])) > 0.1
  5. Set how long to keep logs

    Logs grow fast. Set a retention period in Loki’s limits_config and enable retention in its compactor. Thirty days covers most investigations. Inference access logs are usually the biggest source: if you do not need one line per request, vLLM can turn them off with --disable-uvicorn-access-log.

Before you call it done
  • Every GPU node runs a Fluent Bit pod.
  • {source="kernel"} returns lines from every GPU node, not just one.
  • A Python traceback shows up as one entry, not ten.
  • No label holds request IDs, trace IDs or other one-off values.
  • The Xid and NVML alerts go to a person, not just a channel.
  • You can still read yesterday’s kernel log for a node that was rebooted today.
Find the line yourself

Open GPU Log Watch, trigger a failure and see if you can find the log line before you click the alert. Then try the same failure as metrics in GPU Cluster Watch.

Open the demo →

Related on this site: How to monitor GPUs in Kubernetes and the AI Infrastructure Lab Workbook (section F covers Prometheus and Loki hands-on).

About The Author


Discover more from Journal of Intelligent Infrastructure

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure

Subscribe now to keep reading and get access to the full archive.

Continue reading