Post

My Homelab in 3D: Inside the DGX Spark and the Homelab PC

An interactive 3D tour of my desk, an exploded DGX Spark and an i9 + RTX 5070 Ti homelab PC, plus how the two machines split the work and reach the internet.

My Homelab in 3D: Inside the DGX Spark and the Homelab PC

What actually runs the Cloudutsuk Platform? Two boxes on and under one desk: an NVIDIA DGX Spark the size of a paperback stack, and an Intel tower with an RTX card and pink fans. Instead of another spec list, I’ve built this post around interactive 3D models you can spin, zoom and take apart, with every part clickable for its real specs. At the end there’s a network map showing how the two machines reach each other and the internet without a single open port on my router.

The desk

Start with the whole setup. Drag to orbit, hold Ctrl and scroll (or pinch) to zoom, and click anything. Turn on Cables to see which machine feeds which monitor; the dots show the direction of the signal.

Loading the 3D view…

The desk as of October 2026. Drag to orbit, click any device for its specs, toggle Cables to see what feeds what.
All parts (Homelab desk)
PartDetails
Sit-stand deskElectric sit-stand desk with a walnut top and black frame; both towers sit beneath it on castors.
NVIDIA DGX Spark
spark-0be9 · GB10 Grace Blackwell
The always-on box: local LLMs and the 24x7 Cloudutsuk Platform clusters (core, platform, mon, ai).
CPU: 20 Arm cores (10× Cortex-X925 + 10× Cortex-A725) · GPU: Blackwell (GB10), up to 1 PFLOP FP4 sparse · Memory: 128 GB LPDDR5X unified, 273 GB/s · Storage: 4 TB Samsung NVMe · OS: DGX OS (Ubuntu 24.04.4), driver 580.159 · Network: Wi-Fi 7 (on 5 GHz today); 10 GbE + ConnectX-7 unused · Size: 150 × 150 × 50.5 mm, 1.2 kg, 240 W PSU
Targus DV4K dock
Targus DOCK182 (USB-C DV4K, 100 W)
DisplayLink dock on the Spark's USB-C: drives the two LG monitors, plus the webcam and receivers.
Video: DisplayLink (evdi), 2 outputs · Upstream: USB-C to the DGX Spark
LG UltraWide
LG ULTRAWIDE · 2560×1080
Input 1 from the Spark through the Targus dock, input 2 from the homelab PC's RTX 5070 Ti over HDMI.
LG monitor (top)
LG, mounted above the main screen
Shows the DGX OS wallpaper in the photo; sits on the stand above the main LG.
LG HDR WQHD
LG HDR WQHD · 3440×1440
The main screen: the Spark through the Targus dock, and the homelab PC's motherboard DisplayPort (Intel iGPU) on its second input.
Monitor on armRight-hand monitor on an arm, off in the photo; the webcam sits on top of it.
Logitech Brio 100Webcam on top of the right monitor, connected through the Targus dock.
Portable monitorSmall portable screen at the front left (off in the photo).
Dell laptopWork laptop on the right of the desk; not part of the homelab.
Keyboards and miceThe Spark's keyboard and Sharkoon vertical mouse drive both machines over Input Leap; the PC's Logi Bolt receiver and Sharkoon keyboard are still plugged in too.
EMEET OfficeCore M1AUSB speakerphone for calls, through the Targus dock.
Homelab PC
homelab · 192.168.1.10
Intel workstation for time-bound projects (Shorts Studio, Job Hunter) and Ollama, so the Spark stays free for 24x7 work.
CPU: Intel Core i9-14900KS, 24 cores / 32 threads, 6.2 GHz · Board: ASUS Pro WS W680-ACE IPMI · Memory: 96 GB DDR5 (2× 48 GB G.Skill F5-5200J4040A48G) at 4800 MT/s · GPU: NVIDIA GeForce RTX 5070 Ti, 16 GB GDDR7, 300 W limit · Storage: 2 TB ADATA Legend 960 NVMe · OS: Ubuntu 26.04 LTS, kernel 7.0 · Network: Wi-Fi (Realtek RTL8852BE); 2× Intel I226-LM 2.5 GbE unused
Cooler Master towerOlder Cooler Master tower beside the homelab PC; not part of the setup described here.
USB-C: Spark → Targus dockOne USB-C cable carries video (DisplayLink), USB and audio to the dock.
Dock → LG UltraWideDisplayLink output 1.
Dock → LG HDR WQHDDisplayLink output 2.
PC motherboard DP → LG HDR WQHDThe W680 board's DisplayPort (Intel UHD 770 iGPU) into the main LG's second input.
RTX 5070 Ti HDMI → LG UltraWideThe RTX 5070 Ti's HDMI into the UltraWide's second input.

A few things the model makes obvious that a photo doesn’t:

  • Two machines share two monitors. The LG HDR WQHD (3440×1440) and the LG UltraWide (2560×1080) each have two inputs in use. The Spark reaches both through a Targus DisplayLink dock, and the PC reaches them directly: the WQHD from the motherboard’s DisplayPort, the UltraWide from the RTX card’s HDMI. Switching machines is an input button on the monitor, not a cable swap.
  • The Spark sits on the desk, the PC sits under it. The Spark is 150 mm square and lives under the main monitor. The tower stands on a wooden caddy with castors, next to an older Cooler Master case that isn’t part of the setup.
  • One keyboard and mouse drive both machines. The Spark’s keyboard and Sharkoon vertical mouse are shared with the PC over an encrypted Input Leap link, so the pointer just slides from one screen to the next. The PC’s own Logi Bolt receiver and Sharkoon keyboard are still plugged in too. The setup is in A Virtual KVM for Two Servers with Input Leap.

If you’re wondering how the DisplayLink dock works on an ARM box at all, that was its own afternoon: Getting a Targus DisplayLink Dock Working on the NVIDIA DGX Spark.

Who does what

Here’s the rule I settled on, and it shapes everything else in this post:

The DGX Spark runs anything that has to be up 24x7: the local LLMs and the platform tools (identity, secrets, GitOps, monitoring). The homelab PC takes the time-bound work: projects like Shorts Studio and Job Hunter that run for a while, then get torn down.

The reasoning is mostly about blast radius. The Spark’s clusters are the foundation: Keycloak, OpenBao, Argo CD, Grafana. If a project experiment eats all the memory or wedges Docker, I don’t want my sign-in, my secrets and my dashboards going down with it. Today the project apps still live in the platform cluster on the Spark (you’ll see job-hunter and shorts-studio namespaces there), and moving them to the PC is the next step.

There’s a hardware argument too. The Spark’s 128 GB of unified memory is the scarce, valuable thing: it’s what lets a single box hold models that would never fit on a 16 GB gaming card. Spending it on a Postgres for a side project is a waste. The PC has 96 GB of ordinary DDR5 and 24 fast x86 cores, which is exactly what build-heavy, short-lived workloads want.

 DGX SparkHomelab PC
Role24x7: LLMs and platform toolsTime-bound projects, Ollama
CPU20 Arm cores (10× X925 + 10× A725)i9-14900KS, 24C / 32T, 6.2 GHz
Memory128 GB LPDDR5X unified, 273 GB/s96 GB DDR5 (2× 48 GB)
GPUBlackwell (GB10), shares system memoryRTX 5070 Ti, 16 GB GDDR7
Storage4 TB NVMe (653 GB used)2 TB NVMe (244 GB used)
OSDGX OS (Ubuntu 24.04.4)Ubuntu 26.04 LTS
NetworkWi-Fi 7, 192.168.1.19Wi-Fi 6, 192.168.1.10
Architectureaarch64x86_64

That last row matters more than it looks. Anything I build on the Spark is an ARM image; anything on the PC is x86. Multi-arch builds stop being a nice-to-have once you have both.

Inside the DGX Spark

Pull the Explode slider to lift the lid, cooler and superchip off the board. The internal layout is a simplified schematic (I haven’t opened mine), but the outer size, the ports and every spec in the side panel are real: NVIDIA’s published numbers, cross-checked against lscpu, free and nvidia-smi on the machine.

Loading the 3D view…

Schematic of the DGX Spark (150 × 150 × 50.5 mm). Pull the Explode slider to lift the parts apart; the internal layout is simplified, the specs are real.
All parts (DGX Spark, exploded)
PartDetails
Chassis
150 × 150 × 50.5 mm · 1.2 kg
Metal enclosure 150 mm square; on my desk it sits under the main monitor.
Size: 150 mm × 150 mm × 50.5 mm · Weight: 1.2 kg · Power: 240 W external power supply (GB10 TDP 140 W)
Rear I/OEverything plugs in at the back: in my setup one USB-C goes to the Targus dock and HDMI goes to a monitor.
USB: 4× USB Type-C · Display: 1× HDMI 2.1a; up to 3× DisplayPort over USB-C · Ethernet: 1× RJ-45 10 GbE (unused here, link down) · High-speed: 2× QSFP on ConnectX-7 (unused here)
MainboardCarries the GB10 superchip, its memory, the NIC, Wi-Fi and the SSD slot (layout simplified here).
GB10 Grace Blackwell superchip
NVIDIA GB10 Grace Blackwell superchip
CPU and GPU on one package sharing one pool of memory, which is why a 70B-class model fits without splitting it across devices.
CPU: 20 Arm cores: 10× Cortex-X925 (up to 3.9 GHz) + 10× Cortex-A725 · GPU: Blackwell, 5th-gen Tensor Cores · AI compute: Up to 1 PFLOP FP4 (theoretical, with sparsity) · TDP: 140 W · Seen as: nvidia-smi: NVIDIA GB10 · driver 580.159.03 · ~7 W idle
128 GB LPDDR5X unified memory
128 GB LPDDR5X, 256-bit, 273 GB/s
One coherent pool the CPU and GPU both address; free -h shows 121 GiB usable to Linux, and the GPU takes from the same pool.
Capacity: 128 GB (121 GiB visible to the OS) · Interface: 256-bit LPDDR5X · Bandwidth: 273 GB/s · Gotcha: Bandwidth, not capacity, limits tokens/s on big dense models
4 TB NVMe SSD
Samsung MZALC4T0HBL1 · 3.7 TiB
Holds DGX OS, Docker images, k3d volumes and model weights; 653 GB used today.
Form: M.2 NVMe, self-encrypting · Used: 653 GB of 3.7 TB (19%) · Mounted: / (/dev/nvme0n1p2)
ConnectX-7 SmartNIC
NVIDIA ConnectX-7, 200 Gbps
Meant for clustering two Sparks back to back; I run one Spark, so it's idle.
Ports: 2× QSFP at the back · Speed: Up to 200 Gbps · Use here: None yet
Wi-Fi 7 + Bluetooth 5.4The Spark's only active link today: Wi-Fi on 5 GHz (channel 161, 80 MHz) to the home router, 192.168.1.19.
Interface: wlP9s9 · 192.168.1.19/24 · Gateway: 192.168.1.254 · Also: tailscale0 100.72.155.9 for remote access
Heatsink and fansCools the 140 W GB10; at idle the GPU reads about 49 °C.
Idle GPU: ~49 °C at ~7 W (nvidia-smi)
Top coverChampagne-gold metal lid that closes the box.

The part worth clicking is the GB10. CPU and GPU sit on one package and share one 128 GB pool of LPDDR5X. In practice that means:

  • Capacity is generous, bandwidth is not. 273 GB/s is a fraction of what a discrete GPU’s GDDR7 delivers. Big dense models fit, but tokens per second are bounded by memory bandwidth. Mixture-of-experts models and smaller dense models feel much better than their parameter count suggests.
  • nvidia-smi shows no GPU memory total. You’ll see [N/A] for memory, because there’s no separate VRAM. Linux sees 121 GiB, and the GPU borrows from the same pool. That’s why idle model servers on the ai cluster scale to zero with KEDA instead of holding memory nobody is using.
  • It idles at about 7 W on the GPU. That’s the real reason it gets the 24x7 jobs.

Check the Rear I/O too. The Spark has a 10 GbE port and a ConnectX-7 with two QSFP cages, and right now none of them is connected. Everything goes over Wi-Fi. That’s fine for one box talking to the internet. It gets less fine once the two machines start talking to each other a lot (more on that below).

Inside the homelab PC

Same controls. The case, CPU cooler and PSU are generic shapes because I haven’t recorded their models; everything else came straight off the machine over SSH.

Loading the 3D view…

The Intel workstation that takes the time-bound projects. Pull Explode to separate the parts; case, cooler and PSU are generic shapes.
All parts (Homelab PC, exploded)
PartDetails
CaseMid-tower with three ARGB front fans, on a wooden caddy with castors under the desk.
Front ARGB fansThree front fans with RGB rings: the pink glow in the photo.
ASUS Pro WS W680-ACE IPMI
ATX workstation board, Intel W680, LGA1700 · BIOS 3501
A workstation board with out-of-band management (IPMI card) and ECC support, so the PC can be managed like a small server.
Socket: LGA1700 (12th to 14th gen Core) · Memory: 4× DDR5 DIMM, up to 192 GB, ECC or non-ECC · PCIe: 2× PCIe 5.0 x16 (x16 or x8/x8), 2× PCIe 3.0 x16 (x4), 1× PCIe 3.0 x1 · M.2: 3× M.2 PCIe 4.0 x4, 1× Key E for Wi-Fi · LAN: 2× Intel I226-LM 2.5 GbE (both unused, link down) · Management: IPMI expansion card included · Video out: DisplayPort, HDMI, VGA from the CPU's UHD 770
Intel Core i9-14900KS
24 cores (8P + 16E) / 32 threads · up to 6.2 GHz
Fast single-thread for builds and k3d control planes; it also carries the layers Ollama can't fit in 16 GB of VRAM.
Cores: 24 (8 performance + 16 efficient), 32 threads · Max clock: 6.2 GHz · iGPU: Intel UHD 770 (drives the LG HDR WQHD) · Idle temp: 37 °C package (sensors)
CPU coolerWhatever cools the i9; drawn as a generic tower cooler.
96 GB DDR5
2× 48 GB G.Skill F5-5200J4040A48G
Two 48 GB DIMMs, one per channel, rated 5200 MT/s CL40 and running at 4800 MT/s; two slots free for 192 GB later.
Kit: G.Skill F5-5200J4040A48G (DDR5-5200 CL40) · Slots: Controller0-DIMM1 and Controller1-DIMM1; DIMM0 slots empty · Speed now: 4800 MT/s, below the kit's 5200 rating · Visible: 91 GiB to Linux
NVIDIA GeForce RTX 5070 Ti
GB203 · 16 GB GDDR7 · driver 595.71.05
The PC's model runner: Ollama keeps the small models (qwen3:8b, gemma4) fully on the card and splits the 17 to 20 GB ones with system RAM.
VRAM: 16 GB GDDR7 (16,303 MiB) · Power limit: 300 W · Link: PCIe 5.0 x16 · Output: HDMI to the LG UltraWide · Models here: qwen3-coder:30b, qwen3:32b, deepseek-r1:32b, gemma4:26b, gemma4, qwen3:8b
2 TB ADATA Legend 960
ADATA LEGEND 960 · M.2 PCIe 4.0 NVMe · 1.9 TiB
Boot drive and model store: Ubuntu 26.04, Docker and about 90 GB of Ollama models; 244 GB used.
Used: 244 GB of 1.9 TB (14%) · Mounted: / and /boot/efi
Realtek RTL8852BE Wi-Fi 6The PC's live network link today: Wi-Fi to the home router at 192.168.1.10; the two 2.5 GbE ports are unplugged.
Interface: wlp7s0 · 192.168.1.10/24 · Gateway: 192.168.1.254 · Bluetooth: IMC Networks radio on USB
Power supplyFeeds the i9 and the 300 W RTX card; drawn as a generic ATX unit.

The board is the interesting choice: an ASUS Pro WS W680-ACE IPMI, a workstation board with an IPMI card and ECC support. For a PC that’s going to host clusters, out-of-band management is a genuinely useful extra: you can power-cycle a hung box without walking over to it.

Two details I only noticed while building this model:

  1. The RAM runs at 4800 MT/s, not 5200. The kit is G.Skill F5-5200J4040A48G (DDR5-5200 CL40), but udevadm reports 4800. Two of the four slots are free, so the board can still go to 192 GB.
  2. The RTX doesn’t drive the main monitor. The LG HDR WQHD hangs off the motherboard’s DisplayPort, which is the Intel UHD 770 inside the i9. Only the UltraWide is on the RTX’s HDMI.

The RTX 5070 Ti runs Ollama with qwen3-coder 30b, qwen3 32b, deepseek-r1 32b and the gemma4 models. The 30B-class models are 17 to 20 GB, more than the card’s 16 GB, so Ollama splits them between VRAM and system RAM. That’s where the 96 GB and the fast i9 earn their keep. Ollama only listens on 127.0.0.1:11434, so it’s local to the PC for now.

You can check all of this on your own Linux box without root:

1
2
3
4
5
6
7
8
lscpu | grep -E 'Model name|^CPU\(s\)|max MHz'
cat /sys/class/dmi/id/board_vendor /sys/class/dmi/id/board_name
# RAM kit and speed per slot, readable without sudo on recent systemd
udevadm info -e | grep -E 'MEMORY_DEVICE_[0-9]+_(LOCATOR|SIZE|SPEED_MTS|PART_NUMBER)='
nvidia-smi --query-gpu=name,memory.total,power.limit --format=csv
lsblk -d -o NAME,SIZE,MODEL,TRAN
# which GPU drives which display output
for c in /sys/class/drm/card[0-9]; do echo "$(basename $c) $(basename $(readlink $c/device/driver))"; done

How it’s all connected

The 3D models show hardware; this map shows traffic. Click a machine to open it, click an arrow for the protocol, port and certificate on that hop, or pick a use case on the left and press play.

Laying out the diagram…

Homelab network

  • You (phone or laptop)
  • Internet services
    • Cloudflare (Access + Tunnel edge)
    • Tailscale (coordination server)
  • Home network (192.168.1.0/24)
    • Home router (192.168.1.254)
    • DGX Spark (spark-0be9 · 192.168.1.19): Host services, k3d clusters
    • Homelab PC (homelab · 192.168.1.10): Ollama, Project clusters

Open Grafana from the phone: An internal tool over Tailscale: no public name, no open port.

  1. Keys from Tailscale: Your phone already holds the Spark's WireGuard key and the 172.30.0.0/24 route from the coordination server.
  2. WireGuard to the Spark: The packet goes straight to the Spark, encrypted with WireGuard; nothing touches Cloudflare.
  3. Subnet route to mon: tailscaled forwards to mon's Traefik, which ends TLS with a Let's Encrypt certificate.
  4. Dashboard comes back: The response returns the same way, inside the tunnel.

A public sign-in: A flat public name through Cloudflare Access and the outbound tunnel.

  1. Browser to Cloudflare: The visitor reaches Cloudflare's edge in Mumbai; Access checks them first.
  2. Down the tunnel: Cloudflare sends it down the tunnel the Spark opened; the router has no port forward.
  3. Keycloak answers: cloudflared hands the request to core's Traefik on the bridge, and Keycloak answers it.
  4. Page returns: The response goes back up the tunnel and out of the edge.

The key design choice is that nothing on the internet connects in. The router has no port forwards. Two outbound paths do all the work:

  • Tailscale for me. My phone and laptop join a tailnet, and tailscaled on the Spark advertises the cluster bridge 172.30.0.0/24 as a subnet route. Internal names like grafana.mon.home.cloudutsuk.com resolve to those private addresses and travel inside WireGuard.
  • A Cloudflare Tunnel for anything public. cloudflared runs on the Spark’s host, not inside a cluster, and holds a QUIC connection open to Cloudflare’s Mumbai edges. Public names like auth.cloudutsuk.com come back down that tunnel, after Cloudflare Access has checked who’s asking. The reasons it sits on the host are in ADR 0058.

The PC’s side of the map is mostly amber, meaning planned. The project clusters will be deployed by the Spark’s Argo CD and send telemetry to the Spark’s mon cluster, exactly like the lab clusters do today. The step list is in the product planning post.

Things that bit me while documenting this

Writing this post was supposed to be a quick inventory. A few things turned up anyway:

  • Tailscale remembered a machine that no longer exists. The tailnet still listed homepc as a Windows node, offline for 151 days. The PC had been reinstalled with Ubuntu since then and never rejoined. A stale node is harmless until you try to SSH to it by name and wonder why it times out. The CLI can’t delete another node, so it has to go from the admin console.
  • known_hosts was full of ghosts. Hashed entries (|1|…) don’t tell you which host they belong to. ssh-keygen -F <host> does, so check each candidate name before deleting anything.
  • OpenSSH quietly rewrote my known_hosts. I tested the new entries with -o HostKeyAlgorithms=ssh-ed25519, and OpenSSH’s UpdateHostKeys (on by default) pruned the RSA and ECDSA keys it hadn’t been offered. If you’re verifying host keys, connect normally afterwards and check that all three are still there:
1
2
ssh-keygen -F 192.168.1.10 -l      # list what's pinned for a host
ssh-keyscan 192.168.1.10 2>/dev/null | ssh-keygen -l -f -   # what the host offers now
  • The wired ports are all dark. Both machines are on Wi-Fi, while the Spark has 10 GbE and the PC has two 2.5 GbE ports doing nothing. For internet traffic that’s fine. Once Argo CD on the Spark deploys to clusters on the PC and the PC ships telemetry back, a short cable between them is the cheapest upgrade on this desk.

What’s next

  1. Move Shorts Studio and Job Hunter to the PC. That’s the whole point of the split above.
  2. Add the PC as extra model capacity. The platform plan already allows a second machine as one more LiteLLM route, so callers could reach the RTX models through the same endpoint as the Spark’s vLLM models.
  3. Consider wiring the two machines together. A direct cable or a small 2.5 GbE switch would keep Spark ↔ PC traffic off the Wi-Fi.

Conclusion

Two machines, one rule: the Spark keeps the lights on, the PC does the work that comes and goes. If you’re planning a similar setup, decide which machine is your always-on foundation before you deploy anything to it. Moving the foundation later is far harder than moving a project.

The 3D viewer behind this post is reusable. Each scene is a YAML file of parts and specs, so if you’d like your own rig modelled the same way, the data format is documented at the top of the include.

References

This post is licensed under CC BY 4.0 by the author.