Containerization & deployment¶
Why it matters¶
Every language path's Month 5 (production) is followed by Month 6 (capstone). Between "write the code" and "users use the code" is deployment - the part most curricula skip. This page bridges the language paths and the Container/Kubernetes paths.
The deployment stack, layer by layer¶
A modern deployment is roughly:
Source code → Build artifact → Container image → Image registry →
Cluster manifest → Cluster (Kubernetes / Nomad / docker-compose) →
Runtime → Users
Each layer is a tool category covered somewhere on this site.
The lens, per path¶
Go - single static binary, then deploy¶
Month 5 - Production & Distributed. Go's killer deployment feature: go build produces a single static binary. No JVM, no Python runtime, no NPM modules. Copy the binary to a server (or into a container) and it runs.
Result: the smallest deployment story of any path. A 10MB binary in a 10MB distroless image. Cold start in milliseconds.
What's unique: Go's design directly optimizes for "ship a CLI or service binary."
Java - JAR + JVM, JLink, GraalVM native-image¶
Month 5 - Production. Three deployment shapes:
- Fat JAR + JVM image - the traditional approach. ~200MB image with the JVM bundled. Slow cold start (1-3s).
- JLink runtime image - bundle only the JDK modules you need. Smaller (~80MB), still JVM.
- GraalVM
native-image- AOT-compile to a native binary like Go. ~30MB, milliseconds startup. Trades: reflection requires hints; build time is slow.
What's unique: the breadth of options. Same Java code, three deployment shapes with different trade-offs.
Rust - native binary, smallest images¶
Month 5 - Production Architecture. cargo build --release produces a native binary. With RUSTFLAGS="-C target-feature=+crt-static" it's static; copy to a scratch container; smallest possible image.
What's unique: Rust + scratch image gives the smallest deployable container of any path - single-digit megabytes is common. No runtime, no GC, no JVM, no Python.
Python - wheels + virtualenvs + Docker¶
Month 1 packaging week, Month 3. Build wheels for your platform; install into a virtualenv; bundle into a container. Multi-stage Docker builds for image size (build wheels in a stage with build tools; copy wheels-only into the final image).
What's unique: the "matrix of Python version × CUDA version × OS" complexity. A scientific Python image easily reaches 5GB. Tools like uv (modern Rust-implemented installer) and cibuildwheel (cross-platform wheel-building) ease this.
Containers - the lingua franca¶
Container Internals (senior) - full senior reference. Containers From Scratch (beginner) - the beginner path.
Every language above eventually deploys via containers because: - One image format works across clouds, on-prem, laptops. - Layered storage shares common bytes across services. - The container ecosystem (registries, scanners, runtime tools) is mature.
Kubernetes - orchestration¶
Kubernetes (senior) and Kubernetes From Scratch (beginner).
Beyond a single host, you need orchestration: scheduling pods across nodes, restarting failed ones, rolling updates, service discovery. Kubernetes is the de-facto standard. Alternatives (Nomad, ECS) exist for specific cases.
The CI/CD pattern¶
The repeatable workflow across all languages:
git push → CI builds → tests pass → image built → image pushed to registry →
manifest update → cluster reconciles → deploy complete
Tools at each step:
| Step | Tools |
|---|---|
| CI | GitHub Actions, GitLab CI, CircleCI, Jenkins |
| Tests | language-specific (page 10 of each beginner path) |
| Image build | docker buildx, BuildKit, ko (Go-specific), jib (Java-specific), Buildah |
| Registry | Docker Hub, GHCR, ECR, GAR, ACR |
| Manifest update | manual, or GitOps (Argo CD, Flux) |
| Cluster | Kubernetes, Nomad, ECS, docker-compose for small |
For first contributions, the GitHub Actions → GHCR → Kubernetes path is the most common.
GitOps: the modern pattern¶
Rather than "CI deploys to the cluster," GitOps says:
- The cluster's desired state lives in a git repo (YAML manifests, Helm values).
- A controller in the cluster (Argo CD, Flux) watches the repo.
- The controller reconciles: actual cluster state → match git.
Push to git → controller picks up → cluster converges. Auditable, reproducible, rollback = git revert.
The two big tools: - Argo CD - UI-driven, popular in enterprises. - Flux - CLI-driven, popular in startup-shape orgs.
Both wrap Helm or Kustomize. Beyond beginner; mentioned because every production K8s deployment in 2026 uses one.
Progressive delivery¶
Beyond "deploy this version":
- Rolling update - replace pods one at a time. Kubernetes default. Safe for backward-compatible changes.
- Blue-green - run two parallel deployments; switch traffic atomically. Fast rollback.
- Canary - route a small % of traffic to the new version; verify; gradually shift. Catches problems before all users see them.
- Feature flags - deploy code "off" (behind a flag); flip the flag separately from deploy. Decouples deploy risk from release risk.
Argo Rollouts and Flagger automate canary patterns on Kubernetes. Feature flags via LaunchDarkly, GrowthBook, Unleash.
Observability after deployment¶
Deployment isn't done until you can see your service running. The Observability cross-topic page covers logs, metrics, traces. Production deployments include:
- Health endpoints (
/healthz,/readyz) for the orchestrator to probe. - Metrics endpoint (
/metricsPrometheus format) for scrape. - Structured logs to stdout (let the container runtime ship them).
- Traces to an OTel collector.
Every senior reference path's Appendix A covers the per-language wiring.
The contrasts that teach¶
| Aspect | Go | Java (post-Loom) | Rust | Python |
|---|---|---|---|---|
| Build artifact | static binary | JAR + JVM, JLink, or native | static binary | wheels + venv |
| Image base | scratch / distroless | distroless/java or *-slim |
scratch / distroless | python:3-slim or distroless |
| Image size (typical) | 5-30MB | 80-250MB JVM, 30-50MB native | 5-30MB | 100-500MB |
| Cold start | <1s | 1-3s (JVM), <100ms (native) | <1s | 100-500ms |
| CI build complexity | low | moderate | moderate (longer builds) | moderate (matrix of deps) |
| K8s ecosystem fit | excellent (kubectl, operators in Go) | excellent (Spring Boot + Helm) | growing (operator-rs) | excellent (data + ML workloads) |
The single most clarifying read: build a small service in two languages, deploy both to the same Kubernetes cluster. The Kubernetes parts are identical; the build/image differences teach the per-language ecosystem trade-offs.
What to read first¶
- You ship Go services → Go Month 5 + Containers From Scratch. The simplest, most-direct stack.
- You ship Java services → Java Month 5 + Containers + Kubernetes From Scratch. Pick blocking-on-virtual-threads + Spring Boot 3 + Buildpacks for the easiest path.
- You write infrastructure (operators, controllers) → Kubernetes (senior) + Go from Scratch / Mastery. Most Kubernetes infrastructure code is Go.
- You ship ML / data systems → AI Systems + Containers/K8s. The image-size problem matters more here than anywhere else.
- You're operating someone else's deployed software → Kubernetes (senior) + Observability cross-topic + Performance methodology cross-topic.