Skip to content

Running it on Kubernetes

The same image, the second runtime. Docker Compose trusts its files; Kubernetes refuses at a gate what a file forgot, and this deployment exists to make those refusals real on a laptop before any cloud is involved.

What Docker provides and what Kubernetes adds

scripts/cluster-up.sh
export POSTGRES_PASSWORD=... MANIFEST_IDENTITY_APP_DB_PASSWORD=... MANIFEST_IDENTITY_ADMIN_USERNAME=... MANIFEST_IDENTITY_ADMIN_PASSWORD=...
scripts/deploy-app.sh

The first script fetches kind and kubectl from their canonical releases, verifies their checksums, and brings up a cluster on a digest-pinned node image with Calico installed from a vendored, digest-verified manifest; the default network plugin is disabled because it ignores network policies silently. The second builds the image, loads it into the cluster so no registry is ever consulted, creates the secrets from your environment, refusing to run while one is missing, and waits for readiness. The page is at http://127.0.0.1:8000, published on the loopback interface only, matching the compose posture. scripts/cluster-down.sh removes it all.

What the cluster enforces that compose cannot, each verifiable:

  • Network policy, default deny. Everything is refused except the three flows the system has: operator to application, application to database, and name resolution. Calico enforces; the probes prove:
.tools/kubectl -n manifest-identity exec deploy/app -- python -c "import socket; socket.create_connection(('db', 5432), timeout=5); print('allowed')"
.tools/kubectl -n manifest-identity run probe --image=postgres@sha256:4ef4dbc939d61acea57712655ddb4b4ab27419c913f94cca0cd57cb3ea3c2280 --restart=Never --command -- sleep 300
.tools/kubectl -n manifest-identity exec probe -- timeout 4 bash -c "echo > /dev/tcp/db/5432"   # hangs and dies: denied
  • Admission, two layers. The namespace enforces the restricted Pod Security Standard, and a validating admission policy refuses any image not pinned by digest, with the locally built application image as the one recorded exception (D-047). A privileged pod and an unpinned image are both refused at creation, wording and all:
.tools/kubectl -n manifest-identity run unpinned --image=nginx:latest --restart=Never   # refused by the image-pinning admission policy (D-047)
  • No orchestrator identity to steal. The workloads run under service accounts with no permissions and no mounted token, because the application needs nothing from the Kubernetes API:
.tools/kubectl -n manifest-identity exec deploy/app -- ls /var/run/secrets/kubernetes.io   # No such file or directory

The manifests are schema-validated and posture-linted in the pipeline by kubeconform and kube-linter, both fetched checksum-verified like every other tool.