Chapter 4. Artifact Hygiene at Home

1) Tagging: predictable, unique, effortless

Use a semver + git metadata tag on every build. Example:

<app>:<semver>-<YYYYMMDD>-<shortsha> # e.g., app:0.1.0-20251009-a1b2c3d

Makefile snippet

APP_NAME ?= app
IMAGE_REG ?=
IMAGE_REPO ?= $(IMAGE_REG)ship-securely/$(APP_NAME)
DATE := $(shell date +%Y%m%d)
SHA  := $(shell git rev-parse --short HEAD 2>/dev/null || echo "dev")
TAG  ?= 0.1.0-$(DATE)-$(SHA)

IMAGE := $(IMAGE_REPO):$(TAG)
IMAGE_LATEST := $(IMAGE_REPO):latest

print-image:
	@echo "IMAGE=$(IMAGE)"

2) Immutability: don’t mutate tags

  • Treat every content tag (:0.1.0-…) as immutable.
  • You may move :latest for convenience, but never overwrite a content tag.

Makefile helper

tag-latest:
	@docker tag $(IMAGE) $(IMAGE_LATEST)

3) Retention: keep what matters, prune the rest

  • Keep last N content tags locally (e.g., 20).
  • Keep all evidence (./artifacts/*) for releases.
  • Periodically prune unused images:
docker image prune -f

Optionally log built images:

log-image:
	@mkdir -p artifacts
	@echo "$(IMAGE)" >> artifacts/images-$(DATE).txt

4) Registry or no registry?

Option A — No registry (simplest, default)

Use Minikube’s in-node cache:

mk-image-load:
	minikube -p $(PROFILE) image load $(IMAGE)

Pros: fewer moving parts, faster in class. Cons: not shareable with teammates out of the box.

Option B — Local registry (registry:2)

Spin up a registry on localhost:5000 to push/pull locally.

docker compose file (optional): infra/docker/registry.yaml

version: "3.9"
services:
  registry:
    image: registry:2
    ports: [ "5000:5000" ]
    restart: unless-stopped
    environment:
      REGISTRY_STORAGE_DELETE_ENABLED: "true"

Makefile helpers

reg-up:
	docker compose -f infra/docker/registry.yaml up -d

reg-down:
	docker compose -f infra/docker/registry.yaml down

push:
	@docker tag $(IMAGE) localhost:5000/$(APP_NAME):$(TAG)
	@docker push localhost:5000/$(APP_NAME):$(TAG)

If you use a local registry with Minikube, tell K8s to pull from localhost:5000 by either (a) enabling the Docker driver so node can reach host, or (b) port-forwarding/hostAliases. For this book, Option A is simpler.

5) Store SBOMs & signature proofs under ./artifacts

You already generate:

  • artifacts/sbom.json (Syft)
  • artifacts/cosign-verify.txt (cosign verify)
  • artifacts/trivy-image.json (Trivy)

Keep them per build (zip with date) using your evidence target.


Checklist

  • Content tag format selected and implemented in Makefile
  • Immutable content tags (no overwrites)
  • Decision made: minikube image load (default) vs local registry
  • SBOM & signature evidence present under ./artifacts/