Production Capstone: Build, Secure, Publish, Operate, Observe, and Recover a Complete Dockerized Application: Requirements, Constraints, and Target Architecture
Define the final Docker capstone requirements, threat model, trust boundaries, recovery objectives, evidence contract, and executable production-like architecture before building anything.
Learning objectives
- Define a final capstone as an auditable delivery system rather than “a Compose file that starts.”
- Freeze requirements, threat assumptions, trust boundaries, recovery objectives, version assumptions, and cleanup scope before implementation.
- Design one synthetic application that exercises immutable builds, a local registry, multi-platform metadata, persistent data, secrets/configs, health, resources, logging, and recovery.
- Create a requirements-to-evidence matrix that ties each production claim to an observable Docker/registry/application artifact.
- Separate what this single-host Compose capstone proves from what still belongs to an external orchestrator, secret manager, HA data service, or enterprise control plane.
1. Capstone mission: prove the delivery system, not just the container
The final chapter integrates the course into one reviewable system. The target is a small HTTP application whose source is committed, whose base image is resolved to a digest, whose release is built once as a multi-platform image index, whose SBOM/provenance and signature are bound to the release digest, whose runtime is deployed by digest, whose state survives container replacement, and whose failure/restore behavior is measurable.
Success means another engineer can independently answer: what source produced these bytes, who/what built them, what software is inside, what identity is running, where state and secrets live, which ports and networks exist, how health/resource/log contracts are configured, what backup was restored, and which exact evidence proves each claim?
2. Constraints and excluded dependencies
| Constraint | Capstone decision | Reason |
|---|---|---|
| No paid/cloud dependency | All mandatory work runs on an authorized local Docker host | Keeps the lab reproducible and reviewable |
| No real credentials | Use a fake runtime token and local test signing key | Avoids secret disclosure and external account dependence |
| No production daemon mutation | Local registry binds only to 127.0.0.1; BuildKit HTTP registry trust is scoped to a disposable builder | Avoids global insecure-registry or daemon changes |
| No privileged helper | Dockerfile contains no target-platform RUN step; multi-platform build does not require installing QEMU in this lab | Preserves least privilege |
| No mutable-only release | Deployment uses registry digest; tags are aliases/evidence only | Prevents promotion drift |
| No state in writable layer | SQLite database lives on a named volume | Makes replacement/backup semantics explicit |
| No broad cleanup | Every object has ch42 names/labels and is removed explicitly | Protects unrelated Docker resources |
| Single-host scope | Compose is the production-like runtime; HA scheduling is documented as an external-orchestrator boundary | Avoids pretending a laptop is a cluster |
3. Synthetic application
The app is deliberately small: Python standard library HTTP +
SQLite, with no pip packages. GET /health reports
process/data readiness, GET / returns the greeting and
current counter, and authenticated
POST /increment mutates the SQLite counter using a fake
token mounted as a runtime secret. A marker file in the state volume
can intentionally make health fail for the incident drill.
The container runs as numeric UID/GID 10001, uses a read-only root
filesystem, writes only to /data and /tmp,
drops all Linux capabilities, enables
no-new-privileges, publishes only to loopback, rotates
local logs, and has explicit CPU/memory/PID limits. A companion
observer can reach the app only over an operations network and
records health without receiving the app secret.
4. Target architecture and trust flow
flowchart TD
S[Git source SHA + pinned base digest] --> B[Disposable Buildx / BuildKit builder]
B --> I[Multi-platform image index digest]
B --> A[SBOM + SLSA provenance]
I --> R[127.0.0.1 local registry]
A --> R
R --> V[Scan + signature verification]
V --> P[Release alias points to same digest]
P --> C[Compose deploys immutable digest]
C --> N[frontend + ops networks]
C --> D[named data volume]
C --> Q[config + fake runtime secret]
C --> O[health + bounded logs + resources]
D --> BK[backup checksum]
C --> F[failure drills]
BK --> RR[restore volume + recovery verification]
RR --> H[handoff evidence packet]
The important causal boundary is that the registry digest is the release subject. SBOM, provenance, signature, deployment, rollback, scan results, and incident notes all point back to that immutable subject. A tag can help humans find it, but a tag is never the final identity.
5. Threat model
| Threat / mistake | Control in capstone | Residual limit |
|---|---|---|
| Build from drifting source/base | Git SHA + base image index digest + recorded builder/tool versions | Upstream package repositories are not used during build because the Dockerfile has no RUN step |
| Secret leaks into image/history | Runtime file secret only; no secret ARG/ENV/COPY | Local Compose secret source file still exists on the host and is not encrypted at rest |
| Root/container mutation | UID 10001, read-only rootfs, cap_drop ALL, no-new-privileges | Shared host kernel remains a trust boundary |
| Tag race / rebuild-on-promotion | Deploy/sign/promote exact digest | Local test registry itself is not HA |
| Data loss on container replace | Named volume + backup + restore drill | SQLite single-host model is not a replicated production database |
| Unbounded resource/log growth | CPU/memory/PID limits + local driver rotation | Host capacity planning/central observability remains external |
| Network overexposure | Loopback host publish + separate ops network | No external TLS/load balancer in mandatory lab |
| Evidence erased during incident | Capture IDs/digests/logs/events before repair | Long-term evidence storage is outside the lab |
6. Recovery objectives
Set explicit learning objectives rather than invented enterprise guarantees. The lab target is RPO = backup point: writes after the archive timestamp may be lost. The target RTO is measured, not promised: start a timer before restore-volume creation and stop it only after the recovered app returns the expected persisted counter from the same release digest. Record host and workload conditions so the timing is not presented as universal.
7. Requirements-to-evidence matrix
| Requirement | Primary evidence | Independent verification |
|---|---|---|
| Exact source | Git commit SHA + clean status | git rev-parse HEAD; git status --porcelain |
| Pinned build input | Python base image index digest | imagetools inspect base; Dockerfile/metadata evidence |
| Reproducible builder identity | Buildx/BuildKit version + builder inspect | docker buildx version; docker buildx inspect --bootstrap |
| Multi-platform release | Registry image-index digest + platform manifests | docker buildx imagetools inspect SUBJECT |
| SBOM/provenance | SPDX/SLSA JSON extracted from the release subject | imagetools inspect --format .SBOM/.Provenance |
| Vulnerability status | Trivy version, DB timestamp/output bound to subject | trivy version + JSON result |
| Signature identity | Cosign public key + verify output | cosign verify --key ... SUBJECT |
| Promotion without rebuild | build tag digest == release alias digest | compare imagetools Digest lines |
| Normalized runtime contract | docker compose config output | review service/user/mount/network/resource/security fields |
| Runtime identity | container ID, image digest, user/security/mount/network inspect | docker inspect + compose ps |
| Persistent state | named volume ID + counter value before/after replacement | volume inspect + API response |
| Backup/restore | archive SHA-256 + restored counter + recovery timing | sha256sum + recovered endpoint |
| Observability | health, logs, events, stats sample | compose ps; logs; docker events; docker stats --no-stream |
| Cleanup boundary | pre/post labeled-object inventory | filters by exact names/labels; no prune |
8. Version and platform freeze
Before implementation, capture actual local versions. The course reference baseline is Engine 29.8.1, BuildKit 0.33.0, Buildx 0.37.1, Compose 5.5.1, Trivy 0.74.0, and Cosign 3.1.3. Do not silently substitute these for what your machine runs.
mkdir -p evidence
{
date -u +%Y-%m-%dT%H:%M:%SZ
docker context show
docker version
docker info
docker compose version
docker buildx version
trivy --version 2>/dev/null || true
cosign version 2>/dev/null || true
} > evidence/toolchain.txt
9. Clean-room object namespace
All generated Docker state uses the ch42 prefix or
Compose project ch42cap. The registry is
127.0.0.1:5000, the Buildx builder is
ch42-builder, registry storage is
ch42_registry_data, application state is
ch42cap_state, and restore state is
ch42_state_restore. This is not cosmetic: cleanup and
incident commands can select exact identities.
10. Production boundaries not simulated away
This lab does not claim to provide HA scheduling, external TLS, managed KMS/HSM signing, centralized log/metric retention, multi-node databases, enterprise identity, admission control, or cross-region backups. The handoff must name those gaps and map them to Kubernetes/Helm/OpenShift, an external secret manager, a managed/HA registry, a real observability platform, and production backup infrastructure where appropriate.
11. Go/no-go gate before implementation
| Gate | Go condition |
|---|---|
| Context | Named/inspected authorized local daemon |
| Port 5000 | Available for loopback-only local registry |
| Disk | Enough space for two platform manifests, registry data, scan DB, and backup archive |
| Tools | Docker/Compose/Buildx available; Trivy/Cosign installed from reviewed upstream releases for full capstone |
| Platforms | Builder reports linux/amd64 and linux/arm64 metadata build support; no privileged binfmt install is required by this Dockerfile |
| Cleanup | All planned mutations map to exact ch42 identities |
12. What Lesson 1 freezes
The architecture, evidence matrix, threat assumptions, RPO/RTO interpretation, object namespace, and excluded production dependencies now exist before any release is built. Lesson 2 implements this contract and captures evidence after every major transition rather than reconstructing history at the end.
Knowledge check
Why is the registry digest the primary release identity instead of release-1?
Because the digest is content-addressed and immutable; the tag is a mutable alias that can move.
Why can local Compose secrets satisfy the learning goal but not prove encrypted-at-rest secret management?
Compose can mount the secret as a runtime file, but the source file remains ordinary host storage unless an external secret system protects it.
Why does this capstone avoid RUN instructions in the Dockerfile?
The app uses only Python standard library and numeric metadata, so the multi-platform build can assemble manifests without executing target-architecture binaries or requiring a privileged QEMU installer.
What does RPO = backup point mean?
Any state created after the captured backup can be lost during restore; the capstone must state that limit instead of implying zero data loss.
A container is healthy and the digest is correct. Is the production contract fully proven?
No. Persistent state, external request path, logs/resources, secret/config scope, backup/restore, and other evidence still require independent checks.
Official references and version notes
2026-09-22. The capstone records the learner’s actual versions before execution. Reference baselines used for compatibility discussion are Docker Engine/CLI 29.8.1, BuildKit 0.33.0, Buildx 0.37.1, Docker Compose 5.5.1, Trivy 0.74.0, and Cosign 3.1.3. Packaged Docker Desktop/Engine installations may expose different bundled containerd/runc versions, so the evidence packet records what the active daemon actually reports.
- Docker Engine 29 release notes — current Engine 29.8.1 behavior, fixes, known issues, and component changes.
- Docker Docs — Multi-platform builds — image indexes, native/emulated/cross-compilation strategies, and platform verification.
- Docker Docs — Build attestations — BuildKit SBOM and provenance metadata and image-store/registry requirements.
- Docker Docs — SBOM attestations — SPDX SBOM creation and inspection with Buildx imagetools.
- Docker Docs — Provenance attestations — SLSA provenance modes and inspection.
- docker buildx imagetools inspect — digest, platform, SBOM, and provenance inspection.
- Docker Docs — Use Compose in production — single-host production guidance and production-specific overrides.
- Docker Docs — Use secrets in Compose — runtime secret files and scope; local Compose secrets are not a substitute for an external encrypted secret manager.
- Docker Docs — Volumes — persistent data lifecycle, backup patterns, and container replacement semantics.
- Docker Docs — Resource constraints — CPU, memory, and runtime governance.
- Docker Docs — Configure logging drivers — bounded log retention and driver tradeoffs.
- Docker Docs — Seccomp security profiles — default syscall filtering and safer customization guidance.
- Docker Docs — Rootless mode — threat reduction and operational limits.
- dockerd reference — local registry trust behavior; 127.0.0.0/8 local registries are treated as insecure for testing, but production registries should use trusted TLS.
- Trivy releases — free local vulnerability scanner version identity used in the capstone.
- Cosign releases — signature tooling version identity used in the capstone.
- Open Container Initiative — image/runtime/distribution specifications underlying Docker artifact and runtime interoperability.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.