NVIDIA GPU Architectures Series — Presentation 36

DGX Spark Development Workflow

A practical setup for using Spark as your daily AI development workstation. CUDA toolkit, Python and PyTorch on ARM64, container-first development, VS Code remote, JupyterHub multi-user, fine-tuning at scale within 128 GB, dataset pipelines, and the parts of the workflow that change versus an x86 + discrete-GPU box.

VS Code RemoteJupyterHub PyTorch ARM64Conda / uv NGC containersDevContainer LoRA / QLoRANeMo Triton / Modal
Editor → Container → CUDA → Train / fine-tune → Quantise → Serve
00

Topics We'll Cover

01

Where Spark Fits in Your Workflow

Spark is best as the middle tier of your AI development stack:

Laptop (the IDE)

Your Mac or Linux laptop is just an editor + browser. Don't run heavy local Python kernels — SSH into Spark for everything.

Spark (the workstation)

Where models run. Where you fine-tune. Where you iterate. The ~3-second feedback loop on a 7 B inference test makes prompt-engineering practical.

Cloud (the burst tier)

For pretraining 7 B+, full BF16 ablations, > 70 B-class fine-tunes, big multi-GPU jobs. Rent H100s for hours, not months.

The pattern: iterate on Spark, scale on cloud. Your code lives in git, runs in a container that's identical on Spark and on a cloud DGX H100. The only code change between local and cloud is the model checkpoint path.

02

Connecting from Your Laptop — VS Code Remote SSH

VS Code Remote SSH on Spark is excellent — the X925 cores are fast enough that the language server runs in real time, and ARM64 is fully supported by the VS Code server since 1.75.

~/.ssh/config on your laptop
Host spark
    HostName spark.local         # or 100.64.x.y if Tailscale
    User brendan
    ForwardAgent yes
    ServerAliveInterval 30
    LocalForward 8000 localhost:8000     # vLLM endpoint forwarded
    LocalForward 8888 localhost:8888     # Jupyter forwarded
    LocalForward 3000 localhost:3000     # Open WebUI forwarded

Then in VS Code: Remote-SSH: Connect to Host... → spark. Open ~/code in the workspace. Install Python, Pylance, GitLens, and Dev Containers extensions on the remote (one-time). The first connection takes ~90 s to install the VS Code server; subsequent connects are instant.

If you're outside your home LAN, add Tailscale: Spark gets a stable hostname like spark.tail-scale.ts.net, accessible from anywhere with WireGuard.

03

Containers as the Default Environment

Every NVIDIA-blessed dev workflow on Spark is container-based. Two complementary patterns:

One-shot container

Disposable: spin up, run a script, throw away.

docker run --rm -it --gpus all \
    -v $PWD:/work -w /work \
    nvcr.io/nvidia/pytorch:24.10-py3 \
    python train.py

Long-lived dev container

VS Code Dev Containers: same as one-shot but kept alive while you edit.

// .devcontainer/devcontainer.json
{
  "image": "nvcr.io/nvidia/pytorch:24.10-py3",
  "runArgs": ["--gpus", "all"],
  "mounts": ["source=/opt/models,target=/models,type=bind"]
}
Pin to ARM64 manifests

Always check that the image you're pulling has an ARM64 variant: docker manifest inspect nvcr.io/... should list linux/arm64. The good news: every NGC image since 2024 ships ARM64 alongside x86. Third-party Docker Hub images often don't — budget time to switch or rebuild.

04

Python on ARM64 — Wheels Reality

The pre-2023 ARM64 wheel-availability anxiety is mostly resolved in 2026. Major scientific Python packages ship aarch64 wheels: numpy, scipy, pandas, torch, jax, tensorflow, transformers, accelerate, peft, datasets, sentence-transformers, vLLM, etc.

canonical Spark Python env
curl -fsSL https://astral.sh/uv/install.sh | sh
uv venv -p 3.12 .venv
source .venv/bin/activate
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu130
uv pip install transformers accelerate peft datasets bitsandbytes vllm
05

PyTorch & Friends on Spark

PyTorch on Spark ARM64 + CUDA 13.0 is supported via the cu130 aarch64 wheels (GB10 is sm_121, which CUDA 12.x builds do not target). Compile-once-run-anywhere: code that works on Spark works on a cloud H100 with the same wheel index.

torch.compile on Spark

The Inductor backend compiles to Triton kernels which run on Spark's Blackwell GPU at full speed. Real-world: torch.compile on a Llama fine-tune gives ~15–25% speedup on Spark, similar to a discrete RTX 5090.

06

JupyterHub for Multi-User Spark

One Spark for a small team works well with JupyterHub. Each user gets their own kernel; the unified-memory architecture means model loading is fast and shared.

JupyterHub on Spark with The Littlest JupyterHub
curl -L https://tljh.jupyter.org/bootstrap.py | \
    sudo -E python3 - --admin brendan

# add NVIDIA-flavoured kernel image
sudo tljh-config set user_environment.default_app jupyterlab
sudo tljh-config set images.image nvcr.io/nvidia/pytorch:24.10-py3
sudo tljh-config reload

Memory & GPU sharing patterns:

07

Fine-Tuning — QLoRA, FSDP, NeMo

What you can fine-tune on a single Spark depends on memory, not compute. Rough budgets:

ApproachModel size feasibleToolNotes
Full BF16 fine-tuneup to ~7 BHF Trainer / NeMo SFTweights+grad+Adam state ~16 bytes per param (mixed precision): 7 B → ~112 GB
BF16 + FSDP CPU offloadup to ~13 BHF + Accelerateslow; CPU-GPU traffic over unified-mem fabric
LoRA on BF16 baseup to ~32 Bpeft + transformersfrozen weights at BF16; only adapters train
QLoRA (NF4 base + BF16 LoRA)up to ~70 Bpeft + bitsandbytesthe canonical Spark fine-tuning recipe
NeMo SFTup to ~70 B with DPONeMo + NeMo Alignerpreferred for reproducible large-model alignment
QLoRA on two-Spark pairup to ~120 Bpeft + Accelerate FSDPexperimental; PP=2 with Accelerate works
QLoRA Llama-3.3-70B on a single Spark
accelerate launch \
    --num_processes 1 --mixed_precision bf16 \
    train.py \
    --model_name meta-llama/Llama-3.3-70B \
    --load_in_4bit --bnb_4bit_quant_type nf4 \
    --lora_r 16 --lora_alpha 32 \
    --gradient_checkpointing \
    --per_device_train_batch_size 1 \
    --gradient_accumulation_steps 16 \
    --num_train_epochs 1
08

Dataset Storage & Streaming

Pre-shard your data

The Spark IO pipeline benefits massively from pre-tokenised, pre-shuffled, pre-packed Parquet shards on local NVMe. datasets.save_to_disk() + sharding by 1 GB pieces makes the dataloader's life much easier than streaming raw text.

09

Quantisation Pipeline — HF → NVFP4 / MX-FP4 NIM

You fine-tuned in BF16 / QLoRA. Now you want to deploy in FP4 to a 7 tok/s decode throughput. Blackwell's 5th-gen tensor cores accelerate both NVFP4 and MX-FP4; NVFP4 (16-element blocks, E4M3 scale + per-tensor FP32) is the default in TensorRT-LLM and Transformer Engine, while MX-FP4 (32-element blocks, E8M0 scale) follows the OCP MX spec. The pipeline:

  1. Merge LoRA back into the base weights: peft.merge_and_unload() → full BF16 model on disk.
  2. Convert to FP4 with TensorRT-LLM: quantize.py --kv_cache_dtype fp8 --qformat nvfp4 (or mx_fp4) — calibrates per-microblock scales on a small calibration set.
  3. Build TRT-LLM engine: trtllm-build --checkpoint_dir ./calib --quantization nvfp4 --output_dir ./engine — ~15–25 minutes on Spark for a 70 B.
  4. Wrap as NIM: NVIDIA's nim-builder tool packages a TRT-LLM engine into an OpenAI-compatible NIM container.
  5. Run: docker run --gpus all -p 8000:8000 my-org/llama3.3-70b-mxfp4-nim:v1.

Once you've done this once, you have a reproducible private NIM that ships your fine-tuned model in production-grade form. Same artifact runs on a cloud H100 if you ever burst.

10

Custom Kernels with Triton on Spark

Triton works fully on Spark's Blackwell GPU. The compiler emits PTX targeting GB10's sm_121; you get tensor-core acceleration with a Python-like kernel DSL.

Triton matmul on Spark
import triton, triton.language as tl

@triton.jit
def matmul_kernel(a_ptr, b_ptr, c_ptr, M, N, K,
                  BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr):
    pid = tl.program_id(0)
    # ... tile-based matmul body ...
    tl.store(c_ptr + offsets, accumulator)

Useful patterns Spark-side:

Profile with nsys profile — same workflow as on x86. Triton's autotuner takes longer on Spark's CPU but produces equivalent kernels.

11

Spark Local + Cloud Burst Pattern

The most productive workflow: do all your iteration on Spark, burst to the cloud only for the heavy run.

Iterate on Spark

  • Code in your ~/code/project on Spark.
  • QLoRA fine-tune the 7–13 B variant of your target model with your tiny dataset to validate the script end-to-end.
  • Inspect outputs in JupyterLab; tweak; commit.
  • Total cost: power bill + your time.

Burst to cloud

  • Push branch, trigger CI to run the same script in a cloud DGX H100 (e.g. Lambda Labs, RunPod, OCI).
  • Same container image, same code; just bigger model, full dataset, more epochs.
  • Spend 10–100 GPU-hours, get full-precision result back.
  • Total cost: ~$30–$300 per run.

Why this beats "just always use cloud": iteration cycles. A 30 s sanity-check run on Spark is essentially free; the same on rented cloud GPUs costs $1 in spin-up time alone. You're more willing to experiment when each experiment is cheap.

12

Interactive: Pick Your Workflow