Hardware-variant-aware container image selection for Docker, modeled on Python's PEP 817 wheel variants.
Publish one base version of an image as several variants — say a CUDA 12.8 build, a CUDA 12.6 build, and a CPU-only fallback — and let the client pick the right one for the machine it runs on:
$ docker variant pull registry.example.com/myorg/app:2.1.0
Selected variant "cu128" of registry.example.com/myorg/app version 2.1.0
...
Tagged registry.example.com/myorg/app:2.1.0 and registry.example.com/myorg/app:2.1.0-cu128The same command on a GPU-less machine selects the null (CPU-only)
variant, and on a machine with no compatible variant at all it falls back to
the plain 2.1.0 tag.
PEP 817's wheel-variant concepts are mapped onto standard Docker/OCI primitives — no registry modifications required:
| PEP 817 | docker-variant |
|---|---|
| variant label in the wheel filename | tag suffix: app:2.1.0-cu128 |
variant properties (ns :: feature :: value) |
image config labels: dev.pep817.variant.nvidia.cuda_version_lower_bound=12.8 |
{name}-{version}-variants.json on the index |
variant index: an OCI artifact at app:2.1.0-variants mapping labels → properties + manifest digests |
| provider plugins (hardware detection) | compiled-in detectors (NVIDIA via nvidia-smi, x86-64 levels via CPUID, arm64) |
| null variant | app:2.1.0-null, zero properties, always compatible, ranked last |
Declaring a variant is just Dockerfile labels:
LABEL dev.pep817.variant-label="cu128" \
dev.pep817.variant.nvidia.cuda_version_lower_bound="12.8"docker variant pull fetches the index, detects the local hardware, ranks
the compatible variants (index priorities → system preference →
deterministic tie-breaks), and pulls the winner by digest.
| Command | What it does |
|---|---|
docker variant detect |
Show this machine's variant properties |
docker variant pull REPO:VERSION |
Pull the best-matching variant (--dry-run, --properties-file, --no-fallback) |
docker variant push REPO:VERSION-LABEL |
Push a variant tag and update the repository's variant index |
docker variant list REPO:VERSION |
Table of variants with match ranking for this host |
docker variant inspect REPO:VERSION |
Raw variant index JSON |
docker variant index update REPO:VERSION |
Rebuild the index by scanning the registry's tags |
All registry-touching commands accept --plain-http; loopback registries
use plain HTTP automatically.
Two deployment modes:
- Any V2 registry (Docker Hub, registry:2, Harbor, …):
variant push/index updatemaintain the-variantsindex artifact from the client. - variant-proxy: a stateless reverse proxy in front of the registry
that serves
GET /v2/<name>/_variants/<version>computed on the fly from image labels — publishers then need nothing but plaindocker push. The client tries the endpoint first and falls back to the artifact.
$ variant-proxy --listen :5599 --upstream registry.internal:5000$ make demo # full walkthrough against a throwaway local registry
$ make install-plugin # installs into ~/.docker/cli-plugins => `docker variant ...`
$ make e2e # the same flows as CI assertionsThe demo builds three variants of a runnable hello image, publishes them,
then pulls under three mocked hardware profiles (demo/profiles/*.json) so
you can watch the selection change — and repeats the flow through
variant-proxy with plain docker push only.
$ make test # unit tests (pkg/variant is pure and I/O-free)
$ make vet
$ make e2e # needs dockerDesign and rationale: docs/PLAN.md (roadmap, plugin-vs-fork and registry-side decisions) and docs/DESIGN.md (frozen v1 contracts: label schema, index schema, selection algorithm, ADRs).
Working end-to-end (see e2e/run.sh): publish, list, select, pull,
fallback chain, proxy. Planned next: OCI 1.1 referrers-based index
attachment, index signing (cosign), an exec-based detection-provider
protocol behind explicit opt-in, and a moby fork PoC to demonstrate native
docker pull integration.