This is the post about the site you're reading. I wanted to publish a static blog on the cluster without three things the "obvious" path drags in: a container registry to push a custom image to, a CI pipeline to build that image, and a persistent volume to hold the rendered site. None of them are necessary when the content is fully reproducible from git. Here's the shape, and why each piece is missing on purpose.

The idea: the pod builds itself at startup

There is no custom image. The running Pod assembles the site from git every time it starts, using a chain of initContainers over a shared scratch volume, then a stock web server serves the result:

  1. clone — a stock git image shallow-clones the repo into an emptyDir.
  2. build — a stock static-site-generator image renders the site, in place, inside that clone.
  3. copy — a tiny image copies the rendered output into a second emptyDir.
  4. serve — a stock nginx image serves that second volume, read-only.
initContainers:  git-clone  →  ssg-build  →  copy-output
                      └──── emptyDir: src ────┘     │
                                                emptyDir: site
container:       nginx  (mounts emptyDir: site, readOnly)

Publishing is just git push. CI does one thing: kubectl rollout restart the Deployment. New Pods come up, re-run the init chain, and serve the latest commit. The "build" happens in the Pod, at start, from upstream images.

Why each thing is absent — the actual decisions

No registry. You only need a registry if you're baking a custom image. Here every image is stock and upstream (git, the SSG, nginx); the only thing that varies — your content — is injected at runtime by cloning it. Nothing to build, nothing to push, nothing to store, nothing to keep patched yourself.

No persistent volume. emptyDir is exactly right when the data is disposable and reproducible. The site is regenerated from git on every start, so there is nothing worth persisting. The Pod is genuinely stateless: kill it, it rebuilds identically. PVs exist to keep data a Pod can't regenerate; this Pod can regenerate everything.

No CI image build. Because the build runs in the Pod, CI has no image step. It triggers a rollout and stops. The cluster, not the CI runner, is the build host — which also means the build environment is the same upstream image every time, pinned by tag.

Gotchas worth keeping

  • Build into the SSG's default output dir, not a mounted path. Some generators wipe the output directory before writing (a --force-style clean). If that directory is a mount point, the wipe fails. So build inside the cloned tree and copy the result into the served volume as a separate step — that's why there are two emptyDirs and a copy stage, not one shared mount.
  • Replicas each rebuild independently. With two replicas and a rolling restart, each Pod clones and builds on its own. There's a few seconds of version skew mid-rollout. For a blog that's fine; for anything transactional it wouldn't be.
  • Private repo = read-only token in a Secret. The clone uses a scoped, read-only credential pulled from a Kubernetes Secret. No write access, nothing baked into an image.

The honest trade-offs

This is the right tool only when the build is cheap and the content is git-native. What you pay for the simplicity:

  • Slower Pod start — every start pays clone + build, instead of pulling a prebuilt image.
  • No build gate — a broken commit produces a broken build inside the Pod. If that matters, also build in CI (purely as a validation step) or gate the rollout on a readiness probe so a failed build never serves.
  • Doesn't scale to heavy builds or huge sites — a multi-minute build on every Pod start is the wrong place to be; that's when a real image pipeline earns its keep.

Takeaway

When your artifact is fully reproducible from git, you can collapse the whole registry + PV + CI-image stack into stock images + initContainers + rollout restart. The Pod becomes the build host and git becomes the only source of truth. It's not the answer for every workload — but for a static site it removes three moving parts you'd otherwise own, patch, and debug forever.