SkyPilot at Shopify: Multi-cloud GPUs without the pain

GPUs are annoying. Shopify uses SkyPilot to make them less so: one YAML file, multiple clouds, clean development ergonomics.

Javier Moreno
7 min readintermediate
--
View Original

Overview

Shopify uses SkyPilot, an open-source framework, to manage GPU-intensive ML training workloads across multiple cloud providers (Nebius and GCP). The article details their architecture including a custom SkyPilot plugin for intelligent routing, Kueue-based fair scheduling, automated InfiniBand configuration for H200 GPUs, cost tracking via labels, and developer-friendly environments — all while keeping engineers close to the metal with declarative YAML configs.

What You'll Learn

1

How to use SkyPilot to abstract multi-cloud GPU scheduling with a declarative YAML interface

2

How to build a SkyPilot plugin that routes workloads to different cloud providers based on GPU type

3

How to implement fair-share GPU scheduling across teams using Kueue on Kubernetes

4

How to automatically configure InfiniBand networking for H200 GPU workloads on Nebius

5

Why declarative YAML configs with policy plugins are preferable to elaborate platform UIs for ML infrastructure

Prerequisites & Requirements

  • Understanding of Kubernetes clusters, pods, and job scheduling
  • Familiarity with GPU types (H200, L4) and their use cases in ML training
  • Experience running ML training workloads at scale
  • Familiarity with YAML configuration files and declarative infrastructure
  • Understanding of multi-cloud architecture concepts(optional)
  • Basic understanding of InfiniBand and RDMA for GPU-to-GPU communication(optional)

Key Questions Answered

How does Shopify use SkyPilot for multi-cloud GPU workload management?
Shopify uses SkyPilot as a launcher that schedules ML training jobs onto persistent Kubernetes clusters across Nebius and GCP. Engineers define jobs in YAML specifying GPU requirements, and a custom SkyPilot plugin intercepts requests to route them to the appropriate cloud provider — H200s go to Nebius, L4s and CPU workloads go to GCP — without engineers needing to know which cloud runs their job.
How does SkyPilot route GPU workloads to different cloud providers automatically?
Shopify built a SkyPilot plugin that hooks into the policy engine to intercept every request before it reaches a cluster. The plugin examines the requested accelerator type: H200 requests route to Nebius (which provides InfiniBand interconnect), L4 and CPU-only requests route to GCP. Engineers can override routing with the force_provider_selection flag, but most simply declare their GPU needs and let the platform decide.
How does Shopify handle GPU cost tracking and accountability across ML teams?
Every job requires a showback_cost_owner_ref label that identifies which team or cost center to charge. If this label is missing, the system rejects the job. Teams can see their GPU spend in dashboards and self-correct their usage patterns. This eliminates the need for finance personnel to chase teams for cost attribution, as the cost tracking is enforced at the platform level.
How does Kueue provide fair GPU scheduling across multiple teams on Kubernetes?
Each team maps to a Kueue queue via the ml.shopify.io/quota-group label. Kueue handles fair-share scheduling so every team gets their allocated quota of GPU resources. When clusters are full, Kueue ensures equitable distribution. A priority class system (emergency, interactive, automated-low-priority, lowest) determines preemption order, allowing emergency jobs to displace batch work automatically.
How does Shopify configure InfiniBand for H200 GPU workloads on Nebius?
Shopify's SkyPilot plugin automatically detects H200 workloads and injects the required pod configuration: mounting /dev/infiniband, adding the IPC_LOCK capability for memory locking, and ensuring the Docker image has libibverbs1. This allows GPU-to-GPU communication via RDMA, bypassing the CPU entirely. Engineers don't need to configure InfiniBand manually.
What are SkyPilot development environments and how do they work at Shopify?
Development environments are triggered by adding the ml.shopify.io/dev: true label to a YAML config. They automatically receive interactive priority class for fast scheduling, are exempt from the GPU reaper (which terminates jobs below 20% GPU utilization), and are limited to one GPU. The workflow is simple: sky launch creates the environment, SSH provides access, and sky down or autostop handles cleanup.
What is Shopify's GPU reaper and how does it optimize resource utilization?
The GPU reaper is a service that monitors GPU utilization across training jobs. When it detects jobs running below 20% GPU utilization for extended periods, it terminates them to reclaim resources. This catches runaway or stuck training jobs that waste expensive GPU time. Development environments are exempt from the reaper since low utilization during debugging is expected and normal.
Why does Shopify prefer YAML-based declarative configs over platform UIs for ML infrastructure?
Shopify deliberately chose YAML configs with a policy plugin over an elaborate UI because abstractions that hide complexity eventually become limiting. Engineers stay close to the metal, understand what resources they're requesting, and can debug issues directly. The escape hatch is always available: add a label, override a default, or request a new policy. This approach doesn't bound the ceiling of what's possible.

Key Statistics & Figures

GPU utilization threshold for reaper
20%
Jobs running below 20% GPU utilization for extended periods are terminated by the GPU reaper service
Nebius storage read bandwidth
80 GiB/s
Read bandwidth available on Nebius storage architecture
Nebius storage scalability range
200TB to 2PB
Pay-as-you-grow storage model on Nebius that can scale easily
Volume auto-cleanup period
7 days
Volumes get cleaned up automatically after seven days of disuse
Dev environment GPU limit
1 GPU
Development environments are limited to one GPU; needing eight indicates a training job rather than debugging

Technologies & Tools

Some links below are affiliate links. We may earn a commission if you make a purchase.

ML Infrastructure
Skypilot
Open-source framework for defining and launching ML jobs across clouds via YAML
Container Orchestration
Kubernetes
Persistent clusters across multiple clouds for running GPU workloads
Job Scheduling
Kueue
Kubernetes-native fair-share scheduling with priority-based preemption
Cloud Provider
Nebius
Provides H200 GPUs with InfiniBand interconnect for distributed training
Cloud Provider
GCP
Used for L4 GPU development workloads and CPU-only data processing jobs
Networking
Infiniband
GPU-to-GPU communication via RDMA, bypassing the CPU for fast distributed training
Hardware
Nvidia H200
High-end GPUs on Nebius used for distributed ML training workloads
Hardware
Nvidia L4
GPUs on GCP used for development and lighter workloads
Containerization
Docker
Container images must include libibverbs1 for InfiniBand support on H200 nodes
Development Tools
Jupyter
Notebook-based development environment for debugging on GPU hardware
Configuration
YAML
Declarative job definition format for specifying GPU requirements, labels, and configurations

Key Actionable Insights

1
Use a policy plugin architecture to inject organizational logic into your ML job scheduling system. By intercepting requests before they reach a cluster, you can validate labels, route to different providers, and inject configurations — all without changing the user-facing interface. This keeps the abstraction in the routing layer rather than the interface layer.
Shopify's SkyPilot plugin handles routing, cost tracking, and configuration injection transparently, so engineers just write YAML and run sky launch without worrying about cloud-specific details.
2
Enforce cost attribution at the platform level by requiring a cost-owner label on every job submission. Making cost tracking mandatory (rejecting jobs without it) ensures complete visibility into GPU spend without relying on manual processes or after-the-fact audits.
Shopify requires showback_cost_owner_ref on every job. Teams see their costs in dashboards and self-correct usage patterns, eliminating the need for finance teams to chase down cost attribution.
3
Implement shared caches for model weights and package dependencies across GPU jobs. The first download of a large model like llama-70b gets cached on shared storage, and subsequent jobs start instantly without re-downloading, which saves significant time at scale.
Shopify mounts /mnt/uv-cache for Python packages and /mnt/huggingface-cache for model weights automatically, which adds up when running hundreds of jobs that share common dependencies.
4
Deploy a GPU utilization reaper service that automatically terminates jobs running below a utilization threshold (e.g., 20%) for extended periods. This recovers wasted GPU resources from stuck or runaway training jobs, but make sure to exempt interactive development environments from this enforcement.
Shopify's GPU reaper catches runaway training jobs while exempting dev environments, since low utilization during active debugging is expected and normal behavior.
5
Use Kueue for Kubernetes-native fair-share GPU scheduling with a priority class hierarchy. Define priority tiers (emergency, interactive, automated-low-priority, lowest) that allow emergency jobs to preempt batch work and interactive sessions to schedule faster than automated pipelines.
Kueue solved Shopify's fair scheduling problem better than anything they could have built in-house, handling quota-based scheduling and preemption across teams automatically.
6
When designing multi-cloud GPU infrastructure, keep training data replicated across cloud providers so jobs run where the data already exists. This eliminates cross-cloud data transfer latency and keeps data under your control regardless of which provider runs the workload.
Shopify replicates training datasets across clouds — when training on Nebius, data comes from local volumes; same for GCP — so the routing decision doesn't create a data movement problem.

Common Pitfalls

1
Forgetting to include the cost attribution label (showback_cost_owner_ref) on GPU jobs. Without mandatory cost tracking at the platform level, GPU spend becomes impossible to attribute to specific teams, leading to unaccountable cloud bills and no visibility into resource consumption patterns.
Shopify's system rejects jobs missing this label. While initially annoying, it ensures complete spend visibility and teams self-correct their usage based on dashboard data.
2
Manually configuring InfiniBand settings for H200 GPU workloads instead of automating it. Requiring engineers to mount /dev/infiniband, add IPC_LOCK capability, and ensure libibverbs1 is present is error-prone and slows down onboarding of new ML engineers.
Shopify's plugin detects H200 workloads and injects the correct pod configuration automatically, removing the burden from individual engineers and eliminating configuration drift.
3
Building elaborate UIs or APIs that abstract away all infrastructure complexity for ML engineers. These feel good initially but become limiting prisons that bound what teams can accomplish, because the abstraction inevitably can't anticipate every need.
Shopify chose declarative YAML with a policy plugin instead, keeping engineers close to the metal. The escape hatch is always available — add a label, override a default, or request a new policy.
4
Running a GPU utilization reaper without exempting interactive development environments. Developers actively debugging models often have low GPU utilization between experiments, which would cause the reaper to terminate their sessions and destroy their debugging context.
Shopify exempts dev environments (tagged with ml.shopify.io/dev: true) from the reaper while still catching genuinely runaway or stuck training jobs.
5
Not implementing shared caches for model weights and Python packages across GPU jobs. Without caching, every job redundantly downloads large models like llama-70b, wasting bandwidth and adding significant startup time across hundreds of jobs.
Shopify automatically mounts shared caches (/mnt/uv-cache and /mnt/huggingface-cache) so the first download is cached and subsequent jobs start instantly.

Related Concepts

Multi-cloud Infrastructure
GPU Scheduling
Kubernetes Job Orchestration
Rdma Networking
Infiniband Configuration
Fair-share Resource Scheduling
ML Training Infrastructure
Cloud Cost Management
Declarative Infrastructure
Container Orchestration
GPU Utilization Monitoring
Distributed Training
Model Weight Caching
Cloud Provider Abstraction