Skip to content

Build Images, Caching, Artifacts and Reproducible Delivery ​

Engineering scenario: A service Docker build takes 35 minutes and frequently fails when workers are scaled. Design faster container builds that remain reproducible, secure and traceable to tested source.

Distinguish the layers ​

A CI pipeline has at least four potential bottlenecks: checkout/download, dependency resolution, compile/test, and image/artifact creation. A Docker cache is not a general build cache. A faster image build that silently reuses stale dependencies or publishes an unreviewed tag is a correctness regression.

Cache architecture ​

CacheTypical dataInvalidation / risk
SCM mirrorGit objectsRevision identity, permissions
Package managerDownloaded dependenciesLockfile + registry trust
CompilationObject files/test result cacheCompiler flags, architecture, source hash
Docker layers/BuildKitLayer snapshots/build contextsCOPY order, build arguments, secret leakage
Registry remote cacheCache manifests and layersAccess policy, retention, trust domain
Persistent workspaceCheckout and intermediate filesStale files and cross-build contamination

Cache hits should be observable; benchmark warm and cold builds separately. Avoid one writable shared cache spanning both untrusted pull requests and privileged release builders. A cache exporter can become a registry/storage cost center, so apply retention and cleanup.

BuildKit concept with controlled Dockerfile ​

dockerfile
# syntax=docker/dockerfile:1
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --no-input -r requirements.txt
COPY src/ ./src/
CMD ["python", "-m", "src.main"]

For true repeatability pin the base image digest and package hashes, vet index trust and define architecture. Secrets belong in ephemeral BuildKit secret mounts or a trusted identity mechanism, not ARG or copied into an image layer. This example is simplified and needs a real project plus a verified requirements.txt.

Jenkins publish flow ​

groovy
pipeline {
  agent { label 'trusted-builder' }
  stages {
    stage('Verify') {
      steps { checkout scm; sh './scripts/test.sh' }
    }
    stage('Build') {
      steps {
        sh '''
          set -eu
          docker buildx build --platform linux/amd64 \
            --tag registry.example.com/catalog:build-example \
            --load .
        '''
      }
    }
  }
}

The above intentionally does not push or authenticate. A production publish stage must resolve the exact built digest, authenticate using restricted release credentials, push once, attest the output where required, and hand a digest to deployment. --load generally loads an image into a local Docker image store and is not a universal solution for multi-platform manifests. Avoid mounting the host Docker daemon into untrusted jobs.

Multi-architecture builds ​

Architectures such as arm64 and amd64 may differ in dependency wheels, native compilation, test behavior and emulation performance. Buildx can build multi-platform manifests using suitable builders, but a build that succeeds under emulation doesn't establish runtime compatibility on native nodes. Test each target architecture when practical; record per-platform digest and resulting image index digest.

Failure scenario: build time regression ​

Cache hit rate falls after a dependency manifest moves behind a frequently changed COPY . .. The whole dependency layer is invalidated on every source edit. Reorder Dockerfile layers to copy lockfiles first, then application sources, and measure effect. If cold builds remain slow, inspect registry throttling, network throughput, ephemeral disk I/O and CPU before adding more concurrent builders.

bash
docker buildx inspect
docker buildx du
docker image inspect IMAGE_ID
df -h
df -i
# On an approved agent: capture wall time and cache hit/miss evidence

Artifact identity and retention ​

Archived Jenkins artifacts are useful for short-lived test reports, but high-volume binaries usually belong in an external artifact repository or object store with immutability and retention. Releasing :latest makes rollback ambiguous. Persist source revision, dependency/lockfile identity, builder reference, artifact digest, test result and promotion approval.

Spoken explanation ​

“I'd profile checkout, dependencies, tests and container build separately. Then I'd fix the expensive layer using correct lockfile-based caching and trusted remote cache where justified, preserving immutable inputs. I would isolate build credentials from PR agents, build once, record the digest and promote the tested artifact through a separate authorization boundary. For multi-architecture images I'd validate each target runtime, not just a successful build command.”

References: Docker Build cache · Buildx · Jenkins Pipeline syntax

Practical engineering knowledge, openly shared.