[None][infra] Add Docker BuildKit cache (--cache-to/--cache-from) to reduce Docker image build times
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 14.7k
- Forks
- 2.8k
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 489
Description
Summary
The Docker image build pipeline does not use BuildKit's external cache storage (--cache-to / --cache-from). Every build—on every Kubernetes pod, every PR, every post-merge—starts from scratch with no Docker layer cache. Adding multi-layer registry-based caching could significantly reduce image build times across all CI/CD pipelines.
Current State
How images are built today
The build system uses docker buildx build via docker/Makefile with these default options:
DOCKER_BUILD_OPTS ?= --pull --load
No --cache-to or --cache-from flags are passed anywhere:
docker/Makefile— the%_buildtarget callsdocker buildx buildwithout any cache export/import directivesjenkins/BuildDockerImage.groovy— invokesmake -C docker <target>_<action>without cache flagsjenkins/Build.groovy— builds wheels in-container, no Docker layer caching.github/workflows/— no Docker image builds in GitHub Actions
What is cached today
| Cache Type | Mechanism | Scope |
|---|---|---|
| C++ compilation | ccache (4.9.1) on PVC at /mnt/sw-tensorrt-pvc/scratch.trt_ccache/llm_ccache |
Persistent across builds, 500GB limit |
| pip packages | BuildKit --mount=type=cache,target=/root/.cache/pip |
Per-builder ephemeral (lost when pod dies) |
| Docker layers | None | Rebuilt from scratch on each new pod |
Images built per pipeline
BuildDockerImage.groovy builds 9 images in parallel per pipeline run:
Build Internal release (x86_64 trtllm)— release stageBuild Internal release (SBSA trtllm)— release stageBuild CI Image (x86_64 tritondevel)— tritondevel stageBuild CI Image (SBSA tritondevel)— tritondevel stageBuild CI Image (RockyLinux8 Python310)— tritondevel stageBuild CI Image (RockyLinux8 Python312)— tritondevel stageBuild CI Image (SBSA Ubuntu24.04 Python312)— tritondevel stageBuild NGC devel And release (x86_64)— devel + release stagesBuild NGC devel And release (SBSA)— devel + release stages
Each runs on a fresh Kubernetes pod with Docker-in-Docker (docker:dind), meaning zero Docker layer cache is available at build start.
Dockerfile.multi stage structure
base (nvcr.io/nvidia/pytorch:26.02-py3)
└─ devel (TRT, CUDA toolkit, ccache, MPI4py, UCX, NIXL, etcd)
├─ tritondevel (+ Triton server + Mooncake + CI tools)
├─ wheel (source COPY + build_wheel.py)
│ ├─ release (pip install wheel + benchmarks + examples)
│ │ └─ tritonrelease (+ Triton backend)
│ └─ tritonbuild (Triton backend compilation)
└─ (ngc-devel / ngc-release variants)
The devel stage is the most expensive — it installs TensorRT, CUDA toolkit, CMake, ccache, PyTorch, OpenCV, MPI4py, UCX, NIXL, etcd, and Mooncake. This stage rarely changes between commits.
Proposal
Add --cache-to and --cache-from to Docker buildx builds
Use BuildKit's registry-based cache backend to export and import layer caches. This allows builds on ephemeral Kubernetes pods to benefit from cached layers from previous builds.
Suggested implementation
1. Add cache variables to docker/Makefile:
DOCKER_CACHE_REPO ?=
DOCKER_CACHE_OPTS ?= $(if $(DOCKER_CACHE_REPO),\
--cache-to type=registry,ref=$(DOCKER_CACHE_REPO):cache-$(IMAGE_TAG),mode=max \
--cache-from type=registry,ref=$(DOCKER_CACHE_REPO):cache-$(IMAGE_TAG) \
--cache-from type=registry,ref=$(DOCKER_CACHE_REPO):cache-main \
--cache-from type=registry,ref=$(DOCKER_CACHE_REPO):cache-latest)
Then add $(DOCKER_CACHE_OPTS) to the %_build target's docker buildx build command.
2. Pass cache parameters from Jenkins (BuildDockerImage.groovy):
args += " DOCKER_CACHE_REPO=urm.nvidia.com/sw-tensorrt-docker/tensorrt-llm-cache"
3. Multi-layer cache strategy (3 levels):
| Cache Level | Tag Pattern | Purpose |
|---|---|---|
| Branch-specific | cache-<branch_tag> |
Fastest match for repeat builds on the same branch |
| Main branch | cache-main |
Fallback for new branches — most layers will match |
| Latest stable | cache-latest |
Updated after each successful post-merge build |
Multiple --cache-from sources are tried in order; BuildKit uses the best match automatically.
4. Per-stage cache keys (optional optimization):
For the multi-stage Dockerfile, separate cache tags per stage maximize reuse:
--cache-from type=registry,ref=$(DOCKER_CACHE_REPO):cache-devel-main \
--cache-from type=registry,ref=$(DOCKER_CACHE_REPO):cache-tritondevel-main \
--cache-from type=registry,ref=$(DOCKER_CACHE_REPO):cache-wheel-$(BRANCH_TAG) \
--cache-to type=registry,ref=$(DOCKER_CACHE_REPO):cache-$(STAGE)-$(BRANCH_TAG),mode=max
This is useful because the devel stage changes infrequently while the wheel stage changes on every commit.
Expected impact
| Stage | Typical Duration | With Cache |
|---|---|---|
devel (install TRT, CUDA, UCX, NIXL, etc.) |
Heavy | Fully cached when base image + install scripts unchanged |
tritondevel (Triton server copies + installs) |
Moderate | Cached when Triton version unchanged |
wheel (C++ compilation + wheel build) |
Heavy | Partially cached (source COPY invalidates, but ccache already helps here) |
release (pip install wheel) |
Light | Cached when devel unchanged |
The devel stage is the highest-value target — it's expensive, shared by most images, and changes rarely.
Implementation considerations
mode=maxexports all layers (not just final), maximizing cache hits for multi-stage builds- Registry auth — the cache registry requires the same URM credentials already used in the pipeline
- Cache size — registry-based cache is stored as image layers; old tags can be pruned on a schedule
- Backwards compatibility — when
DOCKER_CACHE_REPOis empty (default), no cache flags are added, so local builds are unaffected - Docker-in-Docker compatibility —
type=registrycache works with DinD since it uses the Docker daemon's registry access (no local filesystem required)
Files to modify
docker/Makefile— addDOCKER_CACHE_REPO,DOCKER_CACHE_OPTSvariables and inject into%_buildjenkins/BuildDockerImage.groovy— passDOCKER_CACHE_REPOand branch-specific cache tags viaargsjenkins/Build.groovy— if any Docker builds are added here in future, same pattern applies
References
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the %_build target in docker/Makefile and the make invocation in jenkins/BuildDockerImage.groovy; trace how image and branch tags are passed through the nine parallel builds. Verify registry cache import/export with ephemeral Docker-in-Docker builders, while confirming that an empty DOCKER_CACHE_REPO preserves local behavior and that the pipeline completes successfully.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, groovy
- Domain
- build-system, ci-cd, devops, infrastructure
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100