Claude Cursor Skill

spark-environment-setup

Set up a working ML training/inference environment on NVIDIA DGX Spark (GB10, aarch64, CUDA 13). Use when installing PyTorch/Unsloth/TRL/vLLM on DGX Spark, hitting libcudart or wheel-ABI errors on aarch64, or choosing between NGC containers and bare pip installs.

LLM Mart · 0 points · 16 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download wshobson-agents-plugins_dgx-spark-ops_skills_spark-environment-setup-554237f.zip · 9 KB
Part of wshobson/agents — 170 skills

Install

skills CLI npx skills add https://github.com/wshobson/agents/tree/main/plugins/dgx-spark-ops/skills/spark-environment-setup
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wshobson-agents@llmmart
Git git clone https://github.com/wshobson/agents.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wshobson/agents collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Spark Environment Setup

DGX Spark ships a GB10 Grace Blackwell chip: aarch64 CPU, SM121 GPU, 128GB unified memory, CUDA 13. This is a narrower and younger platform than a standard x86 CUDA 12 box, so package selection and ABI matching matter more than usual — the wheel ecosystem for aarch64 + CUDA 13 is still filling in.

When to Use This Skill

  • Setting up a fresh Spark box for training or inference.
  • Hitting an import error mentioning libcudart, a missing symbol, or a wheel that "installed fine but won't load."
  • A framework install (PyTorch, Unsloth, TRL, vLLM, xformers) fails, hangs, or silently falls back to CPU.
  • Deciding whether to use an NGC container or bare pip.
  • Restoring a working setup after an OS reinstall or a base-image update, needing to re-verify from scratch.

Each of these accepts the same general fix: match the container/wheel combination to CUDA 13 and SM121, don't fight the ABI.

Container-First Rule

Quick decision, before the detail below:

  • Standard training/inference work → NGC PyTorch container.
  • Unsloth-centric fine-tuning → Unsloth container (it ships the pinned Triton/xformers/transformers combination already validated for that path).
  • Neither fits (custom system package, local IDE interpreter) → bare pip, following the exact sequence further down.

Default to a container. Use nvcr.io/nvidia/pytorch:25.09-py3 as the base for general work — the newest tag confirmed working on this hardware; pull a newer blessed tag if locally available rather than hard-blocking on 25.11-py3. NGC's tag is dated, so running it directly is fine:

docker run --runtime=nvidia --gpus all -it --rm \
  nvcr.io/nvidia/pytorch:25.09-py3

unsloth/unsloth:dgxspark-latest is a moving tag by contrast — resolve and pin its digest before running it for anything reproducible; the bare tag is a discovery step only, not the default invocation. Full pull-inspect-pin sequence and flag rationale/volume mounts for finetuning/ run dirs: references/container-workflow.md. Treat bare pip as the exception.

The reason for the container-first stance is pinning, not convenience. Triton, xformers, and transformers versions interact narrowly with GB10's SM121 target and CUDA 13; a container locks all of them together against a combination already validated on this hardware. Bare pip leaves that resolution to you, one broken import at a time.

When bare pip is warranted, follow the NVIDIA playbook's install sequence verbatim and in order:

pip install "transformers==5.13.1" "peft==0.19.1" "hf_transfer==0.1.9" "datasets==4.3.0" "trl==1.8.0"
pip install --no-deps "unsloth==2026.7.2" "unsloth_zoo==2026.7.2" "bitsandbytes==0.49.2"
pip install -U "torchao==0.17.0"

The second command's --no-deps flag is not optional — letting pip re-resolve Unsloth's dependency tree on aarch64 is a common way to pull in an incompatible torch or triton build. The third line is not optional either: the NGC base image's bundled torchao is too old for current peft's LoRA-attach path (ImportError: ... torchao ... only versions above 0.16.0 are supported) — a hard blocker, not a warning. Every == pin above is load-bearing, taken from the dated known-good version matrix in references/stack-matrix.md (its Last verified date governs staleness) — an unpinned install resolves current PyPI versions well outside what this Unsloth release supports.

Pull a fresh tag when a new blessed release is announced. Rebuild locally from one of the two bases only when a project needs an extra system package layered in — not to "upgrade" a component the image already pins. Details on both paths: references/container-workflow.md.

One more preflight: official DGX Spark playbooks have shipped broken before. Check recent issues on github.com/NVIDIA/dgx-spark-playbooks (and the other resources in references/stack-matrix.md) before trusting a recipe verbatim for a long run.

The ABI Rule

The single most common failure on Spark is a CUDA 12/13 ABI mismatch: a wheel built against libcudart.so.12 loaded on a system that only has libcudart.so.13. The install usually succeeds; the failure surfaces later as a missing-symbol error or a segfault that doesn't obviously point at CUDA.

Fix: pull wheels from download.pytorch.org/whl/cu130 (the cu130-tagged aarch64 builds), or use one of the containers above, which already carry a matched build. Before chasing a stack trace that mentions a CUDA symbol, check which CUDA tag the installed wheel was built against:

python3 -c "import torch; print(torch.version.cuda)"

If that output doesn't start with 13, the ABI mismatch is the first thing to fix. NGC container builds (e.g. nvcr.io/nvidia/pytorch:25.09-py3) build torch internally against CUDA 13 with no +cu130 wheel tag — pip show torch won't say cu130 there, and that absence alone is not a failure.

Typical symptoms:

  • ImportError: undefined symbol referencing a CUDA runtime function.
  • A segfault on the first .cuda() call, no useful traceback.
  • A wheel that installs cleanly, then fails at import time — pip's resolver doesn't check CUDA ABI, only version constraints.
  • Two "identical" environments behaving differently — usually one has a cu130 wheel, the other a cu121/cu124 leftover.

The fix is the same regardless of symptom: match the wheel's CUDA tag to the system, or use a container that already does.

Component Quick Table

Condensed status for the components most likely to come up. Full table with wheel URLs, build flags, the sm_121 vs sm_121a distinction, and the dated known-good version matrix: references/stack-matrix.md.

Component Status
PyTorch ✅ official cu130 aarch64 wheels
bitsandbytes ✅ works out of the box
Triton ✅ needs the TRITON_PTXAS_PATH parameter set
flash-attn ❌ skip pip build; NGC bundles a working one — see spark-training-gotchas G2
xformers source build only (TORCH_CUDA_ARCH_LIST=12.1)
vLLM nightly wheels only
TransformerEngine / NVFP4 train container-only

Everything else — Unsloth, Axolotl, TRL, PEFT — installs cleanly through the container-first path above. LLaMA-Factory and NeMo are fragile on Spark; check upstream issues first.

Verification Commands

Confirm the environment can actually see the GPU before running anything expensive:

import torch
print(torch.cuda.is_available(), torch.version.cuda)

This call returns two values; the exact output format is one line, <bool> <cuda-version>:

True 13.0

If it prints False instead, don't jump straight to a wheel reinstall — ABI mismatch is one cause among several:

Hypothesis Quick check
Runtime/flags nvidia-smi fails in-container too
Device visibility echo $CUDA_VISIBLE_DEVICES
Permissions ls -l /dev/nvidia*
CUDA init state wedged process; retry fresh shell/container
ABI mismatch (usual culprit) torch.version.cuda not 13.x

Check nvidia-smi first — if it doesn't show the GPU, it's one of the first three, not ABI. Reinstall a wheel only once ABI is confirmed. Per-hypothesis detail: references/stack-matrix.md. Run right after the container starts, before installing project-specific packages.

One more check: if Triton kernel compilation fails once training starts, set TRITON_PTXAS_PATH=/usr/local/cuda/bin/ptxas and retry — see references/stack-matrix.md for the full workaround list.

Next Steps

A verified environment is only the starting point. See also: spark-training-gotchas for failure preflights before a training run, and spark-memory-thermal-ops for unified-memory OOMs and thermal throttling during long ones.

Files (agents)
  • references
    • container-workflow.md 4.1 KB
      Last verified: 2026-07-14 — refresh when the blessed container tags change.
      
      # Container Workflow
      
      Concrete `docker run` invocations for the two blessed images, plus the bare-pip fallback.
      
      ## NGC PyTorch container
      
      General-purpose training/inference base:
      
      ```bash
      docker run --runtime=nvidia --gpus all -it --rm \
        --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
        -v "$(pwd)/finetuning:/workspace/finetuning" \
        nvcr.io/nvidia/pytorch:25.09-py3
      ```
      
      **Tag guidance:** `25.09-py3` is the tag actually verified working on this hardware (torch `2.9.0a0+50eac811a6.nv25.09`, CUDA 13.0 baked in, matches the ABI Rule's expectations — no functional delta observed for the packages exercised in fine-tuning workflows). Treat `SKILL.md`'s mention of a newer blessed tag as guidance to pull when locally available, not a hard requirement — if the cited newer tag isn't locally cached and pulling isn't practical, fall back to the newest available `25.x` tag and record the gap in the run's notes rather than blocking on it.
      
      - `--runtime=nvidia --gpus all` gives the container access to the GB10 GPU; without it, PyTorch inside the container will report no CUDA device even though the host sees one fine.
      - `--ipc=host` and the `ulimit` flags avoid shared-memory starvation for PyTorch's DataLoader workers.
      - Mount the repo's `finetuning/` run directory so checkpoints and logs land on the host filesystem, not inside the ephemeral container layer — the `--rm` flag deletes the container (and anything not mounted out) on exit.
      
      ## Unsloth container
      
      For Unsloth-centric fine-tuning runs, prefer the purpose-built image over the generic NGC one — it ships the pinned Triton/xformers/transformers combination already validated for this hardware.
      
      **`dgxspark-latest` is a moving tag, unlike the NGC image's dated `25.09-py3` tag above.** Don't run it directly as the invocation you'll rely on for a real run — resolve and pin its digest first, then run by digest:
      
      ```bash
      # 1. Discovery step: pull the moving tag and confirm it starts.
      docker pull unsloth/unsloth:dgxspark-latest
      
      # 2. Resolve the tag to its current digest.
      docker inspect --format='{{index .RepoDigests 0}}' unsloth/unsloth:dgxspark-latest
      # -> unsloth/unsloth@sha256:<resolved digest>
      
      # 3. Run by digest — this is the reproducible invocation.
      docker run --runtime=nvidia --gpus all -it --rm \
        --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
        -v "$(pwd)/finetuning:/workspace/finetuning" \
        unsloth/unsloth@sha256:<resolved digest>
      ```
      
      Substitute the pinned `@sha256:...` digest for the tag in CI or any pipeline where reproducibility matters — a run recorded against `dgxspark-latest` by tag alone cannot be reproduced later if the tag has moved on. Re-resolve and re-pin the digest whenever picking up a new blessed release (see "Pull vs rebuild" below).
      
      Same flag rationale as the NGC invocation above. Prefer this over rebuilding a custom Unsloth image from the NGC base.
      
      ## Pull vs rebuild
      
      Pull a fresh tag when: a new blessed release is announced, or you're chasing a bug that a recent tag's changelog says it fixes.
      
      Rebuild locally (starting `FROM` one of the two images above) when: you need an extra system package or Python dependency layered in for a specific project, and that dependency doesn't conflict with the pinned training stack. Don't rebuild to "upgrade" a component that the base image already pins — that reintroduces the version-matrix problem the container exists to avoid.
      
      ## Bare-pip escape hatch
      
      If a container genuinely doesn't fit (see `SKILL.md`'s Container-First Rule), isolate the environment with `uv` rather than the system Python, and follow the NVIDIA playbook install sequence from `SKILL.md` inside it.
      
      Caveat: if `uv` insists on a dependency version that conflicts with what the playbook pins (a common outcome given how young the aarch64/CUDA-13 wheel ecosystem is), use `uv pip install --override` to force the pinned versions through rather than letting the resolver silently substitute an incompatible build. Verify the result with the Verification Commands section of `SKILL.md` before trusting the environment.
      
    • stack-matrix.md 6.9 KB
      Last verified: 2026-07-14 — refresh when CUDA, PyTorch, or Unsloth major versions change.
      
      # Spark Stack Matrix
      
      Full component-by-component status for the ML training/inference stack on DGX Spark (GB10, SM121, aarch64, CUDA 13). This is the detail table behind the "Component Quick Table" in `SKILL.md`.
      
      | Component | Status | Notes |
      |---|---|---|
      | PyTorch (cu130, aarch64) | ✅ | Official wheels at `download.pytorch.org/whl/cu130`. Matches the system CUDA 13 ABI — see the ABI Rule in `SKILL.md`. |
      | bitsandbytes | ✅ | 0.48+ works out of the box. |
      | Triton | ✅ (with env var) | Needs `TRITON_PTXAS_PATH=/usr/local/cuda/bin/ptxas` set, or kernel compilation fails to find `ptxas`. |
      | flash-attn | ❌ skip | No sm_121 kernels shipped or buildable yet. PyTorch's SDPA backend is faster on this hardware anyway — don't spend time chasing a flash-attn build. |
      | xformers | source build only | No prebuilt aarch64/SM121 wheel. Build with `TORCH_CUDA_ARCH_LIST=12.1` set, or the build targets the wrong architecture and either fails or silently produces non-functional kernels. |
      | vLLM | nightly wheels only | Use `wheels.vllm.ai/nightly/cu130`. The SM121 fix landed in the nightly channel around 2026-06; stable/release wheels predate it. |
      | TransformerEngine / NVFP4 training | container-only | Not practical via bare pip; use the NGC PyTorch container. `NVFP4BlockScaling` targets SM100 — treat SM121 support as caveated, not guaranteed. |
      | Unsloth | ✅ (container preferred) | Official Docker image `unsloth/unsloth:dgxspark-latest` (a moving tag — resolve and pin its digest for reproducible/CI use, see `references/container-workflow.md`), or the NVIDIA playbook pip sequence (see `SKILL.md`). Bare pip installs have hit torchcodec and GPU-detection gotchas. |
      | Axolotl / TRL / PEFT | ✅ | Standard install, no special handling needed. |
      | LLaMA-Factory / NeMo | fragile / in progress | Known to be unreliable on this platform as of this writing; expect breakage and check upstream issues before depending on either for a run. |
      
      ## Known-Good Version Matrix (Dated)
      
      `SKILL.md`'s bare-pip sequence pins `datasets`/`trl` explicitly
      for a reason: an unpinned `pip install transformers peft
      hf_transfer datasets trl accelerate` resolves current PyPI
      versions of `transformers`/`trl`/`datasets` that sit well
      outside what a given Unsloth release declares support for — pip
      installs them anyway and only warns after the fact. The
      combination below was confirmed working end-to-end (bf16 LoRA
      load + attach + a full SFT run) on `nvcr.io/nvidia/pytorch:25.09-py3`
      as of the date above; treat it as a dated snapshot to re-verify,
      not a permanent pin:
      
      | Package | Verified-working version |
      |---|---|
      | `transformers` | 5.13.1 |
      | `trl` | 1.8.0 |
      | `peft` | 0.19.1 |
      | `datasets` | 4.3.0 (pin as-is; not re-verified independently of the combination above) |
      | `unsloth` / `unsloth_zoo` | 2026.7.2 |
      | `torchao` | 0.17.0 (pure-Python wheel; NGC base image ships 0.13.0+git, too old — `pip install -U torchao` after the Unsloth line) |
      | `bitsandbytes` | 0.49.2 |
      | `hf_transfer` | 0.1.9 (current stable; see the deprecation note below before relying on it) |
      
      If a bare-pip install lands on a different combination than
      this table (pip resolver drift is expected as new releases
      ship), re-run the load+LoRA-attach smoke test in `SKILL.md`'s
      Verification Commands before trusting the environment, and
      check `gh issue list --repo NVIDIA/dgx-spark-playbooks` for a
      version-skew report matching the symptom before assuming it's
      novel.
      
      **`HF_HUB_ENABLE_HF_TRANSFER` is deprecated on `huggingface_hub`
      1.23+.** Setting it now only produces `FutureWarning: The
      HF_HUB_ENABLE_HF_TRANSFER environment variable is deprecated ...
      Please use HF_XET_HIGH_PERFORMANCE instead`, and downloads route
      through Xet rather than hf_transfer regardless. This is cosmetic
      (downloads still succeed, and fast) on current `huggingface_hub`
      — stale task instructions or older recipes that still reference
      `hf_transfer`-based env setup should be read as intent ("make
      downloads fast"), not a literal current-API requirement; set
      `HF_XET_HIGH_PERFORMANCE=1` instead on `huggingface_hub` 1.23+.
      
      ## GPU-Detection False Negative: Per-Hypothesis Detail
      
      The full discriminating check behind `SKILL.md`'s Verification
      Commands hypothesis table, in the order to work through them:
      
      1. **Runtime/flags.** If `docker run` was missing
         `--runtime=nvidia --gpus all`, `nvidia-smi` run *inside* the
         container fails or shows no devices even though the host sees
         the GPU fine. Fix: re-run with both flags.
      2. **Device visibility.** `echo $CUDA_VISIBLE_DEVICES` — an
         empty string set explicitly (not merely unset) hides all
         devices from CUDA; a stale index (e.g. `1` on a single-GPU
         box) hides the only device present. Fix: `unset
         CUDA_VISIBLE_DEVICES` or set it to `0`.
      3. **Permissions.** `ls -l /dev/nvidia*` — missing entries or a
         `Permission denied` on read means the container/user can't
         open the device nodes (common when running rootless or with a
         restrictive seccomp/AppArmor profile). Fix: match the host's
         device-cgroup rules, or don't run rootless for GPU workloads.
      4. **CUDA init state.** A prior process that crashed mid-kernel
         can leave the driver's CUDA context wedged for that process
         tree. Retrying in a fresh shell or a freshly started container
         (not just a new Python process in the same shell) rules this
         out cheaply before assuming anything deeper is wrong.
      5. **ABI mismatch.** The last hypothesis to check, not the
         first: `python3 -c "import torch; print(torch.version.cuda)"`
         not starting with `13` confirms a `libcudart.so.12`-linked
         wheel on a CUDA-13-only system — see the ABI Rule in
         `SKILL.md`. This is the only one of the five that a wheel
         reinstall actually fixes; reinstalling before ruling out 1-4
         wastes a cycle without changing the outcome if the real cause
         is a flag, an env var, or a permission.
      
      A torchcodec/driver interaction is the most frequently reported
      instance of (5) on this hardware specifically — see
      `gh issue list --repo NVIDIA/dgx-spark-playbooks` for current
      reports before assuming a novel cause.
      
      ## sm_121 vs sm_121a
      
      GB10's GPU identifies as `sm_121`. Some newer kernel features — notably NVFP4's native `cvt.e2m1x2` conversion instruction — require code compiled for `sm_121a`, a superset target, not plain `sm_121`. If NVFP4 inference is ~32% slower than FP8 on this hardware, this is why: the kernel likely wasn't compiled with the `a` variant. Check the build flags of whatever wheel or container you're using before assuming the hardware itself is the bottleneck.
      
      ## Canonical resources
      
      - `github.com/NVIDIA/dgx-spark-playbooks`
      - `build.nvidia.com/spark/unsloth`
      - `github.com/natolambert/dgx-spark-setup`
      - `github.com/albond/DGX_Spark_Unsloth_Lossless_Speedup`
      - `github.com/NvMayMay/nvfp4-lora-spark`
      
      Official playbooks have shipped broken before. Check each repo's recent issues before starting a long run, not after it fails.
      
  • SKILL.md 7.9 KB
    ---
    name: spark-environment-setup
    description: Set up a working ML training/inference environment on NVIDIA DGX Spark (GB10, aarch64, CUDA 13). Use when installing PyTorch/Unsloth/TRL/vLLM on DGX Spark, hitting libcudart or wheel-ABI errors on aarch64, or choosing between NGC containers and bare pip installs.
    ---
    
    # Spark Environment Setup
    
    DGX Spark ships a GB10 Grace Blackwell chip: aarch64 CPU, SM121
    GPU, 128GB unified memory, CUDA 13. This is a narrower and
    younger platform than a standard x86 CUDA 12 box, so package
    selection and ABI matching matter more than usual — the wheel
    ecosystem for aarch64 + CUDA 13 is still filling in.
    
    ## When to Use This Skill
    
    - Setting up a fresh Spark box for training or inference.
    - Hitting an import error mentioning `libcudart`, a missing
      symbol, or a wheel that "installed fine but won't load."
    - A framework install (PyTorch, Unsloth, TRL, vLLM, xformers)
      fails, hangs, or silently falls back to CPU.
    - Deciding whether to use an NGC container or bare pip.
    - Restoring a working setup after an OS reinstall or a
      base-image update, needing to re-verify from scratch.
    
    Each of these accepts the same general fix: match the
    container/wheel combination to CUDA 13 and SM121, don't fight
    the ABI.
    
    ## Container-First Rule
    
    Quick decision, before the detail below:
    
    - Standard training/inference work → NGC PyTorch container.
    - Unsloth-centric fine-tuning → Unsloth container (it ships
      the pinned Triton/xformers/transformers combination already
      validated for that path).
    - Neither fits (custom system package, local IDE interpreter)
      → bare pip, following the exact sequence further down.
    
    Default to a container. Use `nvcr.io/nvidia/pytorch:25.09-py3`
    as the base for general work — the newest tag confirmed working
    on this hardware; pull a newer blessed tag if locally available
    rather than hard-blocking on `25.11-py3`. NGC's tag is dated, so
    running it directly is fine:
    
    ```bash
    docker run --runtime=nvidia --gpus all -it --rm \
      nvcr.io/nvidia/pytorch:25.09-py3
    ```
    
    `unsloth/unsloth:dgxspark-latest` is a *moving* tag by
    contrast — resolve and pin its digest before running it for
    anything reproducible; the bare tag is a discovery step only,
    not the default invocation. Full pull-inspect-pin sequence and
    flag rationale/volume mounts for `finetuning/` run dirs:
    `references/container-workflow.md`. Treat bare pip as the exception.
    
    The reason for the container-first stance is pinning, not
    convenience. Triton, xformers, and transformers versions
    interact narrowly with GB10's SM121 target and CUDA 13; a
    container locks all of them together against a combination
    already validated on this hardware. Bare pip leaves that
    resolution to you, one broken import at a time.
    
    When bare pip is warranted, follow the NVIDIA playbook's
    install sequence verbatim and in order:
    
    ```bash
    pip install "transformers==5.13.1" "peft==0.19.1" "hf_transfer==0.1.9" "datasets==4.3.0" "trl==1.8.0"
    pip install --no-deps "unsloth==2026.7.2" "unsloth_zoo==2026.7.2" "bitsandbytes==0.49.2"
    pip install -U "torchao==0.17.0"
    ```
    
    The second command's `--no-deps` flag is not optional —
    letting pip re-resolve Unsloth's dependency tree on aarch64 is
    a common way to pull in an incompatible torch or triton build.
    The third line is not optional either: the NGC base image's
    bundled `torchao` is too old for current `peft`'s LoRA-attach
    path (`ImportError: ... torchao ... only versions above 0.16.0
    are supported`) — a hard blocker, not a warning. Every `==` pin
    above is load-bearing, taken from the dated known-good version
    matrix in `references/stack-matrix.md` (its `Last verified` date
    governs staleness) — an unpinned install resolves current PyPI
    versions well outside what this Unsloth release supports.
    
    Pull a fresh tag when a new blessed release is announced.
    Rebuild locally from one of the two bases only when a project
    needs an extra system package layered in — not to "upgrade" a
    component the image already pins. Details on both paths:
    `references/container-workflow.md`.
    
    One more preflight: official DGX Spark playbooks have shipped
    broken before. Check recent issues on
    `github.com/NVIDIA/dgx-spark-playbooks` (and the other
    resources in `references/stack-matrix.md`) before trusting a
    recipe verbatim for a long run.
    
    ## The ABI Rule
    
    The single most common failure on Spark is a CUDA 12/13 ABI
    mismatch: a wheel built against `libcudart.so.12` loaded on a
    system that only has `libcudart.so.13`. The install usually
    succeeds; the failure surfaces later as a missing-symbol error
    or a segfault that doesn't obviously point at CUDA.
    
    Fix: pull wheels from `download.pytorch.org/whl/cu130` (the
    cu130-tagged aarch64 builds), or use one of the containers
    above, which already carry a matched build. Before chasing a
    stack trace that mentions a CUDA symbol, check which CUDA tag
    the installed wheel was built against:
    
    ```bash
    python3 -c "import torch; print(torch.version.cuda)"
    ```
    
    If that output doesn't start with `13`, the ABI mismatch is the
    first thing to fix. NGC container builds (e.g.
    `nvcr.io/nvidia/pytorch:25.09-py3`) build torch internally
    against CUDA 13 with no `+cu130` wheel tag — `pip show torch`
    won't say `cu130` there, and that absence alone is not a failure.
    
    Typical symptoms:
    
    - `ImportError: undefined symbol` referencing a CUDA runtime
      function.
    - A segfault on the first `.cuda()` call, no useful traceback.
    - A wheel that installs cleanly, then fails at import time —
      pip's resolver doesn't check CUDA ABI, only version constraints.
    - Two "identical" environments behaving differently — usually one
      has a cu130 wheel, the other a cu121/cu124 leftover.
    
    The fix is the same regardless of symptom: match the wheel's
    CUDA tag to the system, or use a container that already does.
    
    ## Component Quick Table
    
    Condensed status for the components most likely to come up.
    Full table with wheel URLs, build flags, the sm_121 vs sm_121a
    distinction, and the dated known-good version matrix:
    `references/stack-matrix.md`.
    
    | Component | Status |
    |---|---|
    | PyTorch | ✅ official cu130 aarch64 wheels |
    | bitsandbytes | ✅ works out of the box |
    | Triton | ✅ needs the `TRITON_PTXAS_PATH` parameter set |
    | flash-attn | ❌ skip pip build; NGC bundles a working one — see `spark-training-gotchas` G2 |
    | xformers | source build only (`TORCH_CUDA_ARCH_LIST=12.1`) |
    | vLLM | nightly wheels only |
    | TransformerEngine / NVFP4 train | container-only |
    
    Everything else — Unsloth, Axolotl, TRL, PEFT — installs
    cleanly through the container-first path above. LLaMA-Factory
    and NeMo are fragile on Spark; check upstream issues first.
    
    ## Verification Commands
    
    Confirm the environment can actually see the GPU before
    running anything expensive:
    
    ```python
    import torch
    print(torch.cuda.is_available(), torch.version.cuda)
    ```
    
    This call returns two values; the exact output format is one
    line, `<bool> <cuda-version>`:
    
    ```text
    True 13.0
    ```
    
    If it prints `False` instead, don't jump straight to a wheel
    reinstall — ABI mismatch is one cause among several:
    
    | Hypothesis | Quick check |
    |---|---|
    | Runtime/flags | `nvidia-smi` fails in-container too |
    | Device visibility | `echo $CUDA_VISIBLE_DEVICES` |
    | Permissions | `ls -l /dev/nvidia*` |
    | CUDA init state | wedged process; retry fresh shell/container |
    | ABI mismatch (usual culprit) | `torch.version.cuda` not `13.x` |
    
    Check `nvidia-smi` first — if it doesn't show the GPU, it's one
    of the first three, not ABI. Reinstall a wheel only once ABI is
    confirmed. Per-hypothesis detail: `references/stack-matrix.md`.
    Run right after the container starts, before installing
    project-specific packages.
    
    One more check: if Triton kernel compilation fails once
    training starts, set
    `TRITON_PTXAS_PATH=/usr/local/cuda/bin/ptxas` and retry — see
    `references/stack-matrix.md` for the full workaround list.
    
    ## Next Steps
    
    A verified environment is only the starting point. See also:
    `spark-training-gotchas` for failure preflights before a
    training run, and `spark-memory-thermal-ops` for unified-memory
    OOMs and thermal throttling during long ones.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related