Fourth phase of Arc 3. Where everything composes. The longest phase of the year.

If Phases 17-19 installed the primitives, Phase 20 is where they compose into the K8s-native ecosystem that basecamp runs on. Linux processes (Phase 17) become pods. TCP/HTTP (Phase 18) becomes Services and Ingress. Containers (Phase 19) become the unit of scheduling. And on top of all of it, the control-loop pattern, the central insight every later year leans on.

This phase brings the basecamp substrate fully alive in the K8s-native pattern: K3s + Flux + CloudNativePG + Redis Operator + Karpenter + Keda + Prometheus + Grafana. Every stateful workload runs under an operator. Every config lives in Git, reconciled by Flux. Every cluster resource is a CRD. By phase end you can debug a Pod stuck in Pending from first principles, and you’ve shipped beacon as the first real service on basecamp.

It’s the longest phase of Arc 3, and it earns every week.


Prerequisites

  • All of Phases 17-19 complete; container from-scratch exercise done
  • Hardware upgraded: 32GB RAM + 1TB external SSD (before starting this phase)
  • You accept: you are not learning Kubernetes. You are learning what declarative orchestration is, with K8s as the canonical implementation and control-loops as the operating principle. The K8s-native ecosystem is the convergent tool set; learn the patterns underneath.

Why this phase exists

Kubernetes is the substrate Years 4-5 build on, and the substrate every later employer’s platform will probably also build on. It is also a giant, frequently-cargo-culted abstraction unless you understand the pattern underneath: control loops reconciling desired state in etcd against observed state in the cluster, forever.

This phase makes that pattern concrete via the K8s-native ecosystem: every component is a CRD-driven controller. Operators (CloudNativePG, Redis Operator) manage stateful workloads. Flux reconciles Git to cluster state. Karpenter reconciles node demand. Keda reconciles workload scaling against external signals. The pattern is consistent across every component you operate.


The pattern-first frame

Same eight steps as every phase.


1. PROBLEM

You have containers (Phase 19). You want to run them across a fleet of machines: self-healing, declaratively scaled, with service discovery, rolling deploys, resource isolation, network policy, all reproducibly described in version control.

That’s the orchestration problem. Kubernetes is one solution. Nomad is another. ECS / Cloud Run are proprietary takes. The pattern is declarative orchestration via control loops; K8s is the tool that won the 2020s, and the K8s-native ecosystem (operators + CRDs + controllers everywhere) is the design language that emerged on top.


2. PRINCIPLES

2.1 Control loops

A control loop watches actual state, compares it to desired state, acts to converge. Every K8s controller, built-in or third-party, is some flavor of this loop.

→ Pattern: control-loops: DEEP target this phase (the central pattern of the program)

Investigate:

2.2 Declarative vs imperative

You say what you want; the tool figures out the diff. You don’t say “create 3 pods, restart any that fail.”

→ Pattern: declarative-vs-imperative-infrastructure

Investigate:

2.3 etcd as source of truth

All cluster state lives in etcd. Every K8s “knowledge” comes from there. Every action is a controller noticing an etcd change.

Investigate:

2.4 The operator pattern

An operator is a controller (or set of controllers) that manages a complex stateful workload through a custom CRD. CloudNativePG’s Cluster CRD reconciles Postgres clusters. Redis Operator’s RedisCluster CRD reconciles Redis clusters. Same shape, different domain.

→ This is the meta-pattern of the K8s-native ecosystem. Every basecamp module (substrate through the AI tier) follows it.

Investigate:

2.5 Service as mediator + Network Policy

A Service is a stable name + IP for a set of Pods. kube-proxy + iptables/IPVS/eBPF make traffic flow. NetworkPolicy constrains who-can-call-who at L4 (and L7 with Cilium).

→ Patterns: mediation reinforced; service-discovery, load-balancing, network-policy

Investigate:

2.6 GitOps via Flux

Desired state lives in Git. A controller (Flux) reconciles cluster against Git. git push is the deploy mechanism. Flux uses composable CRDs: GitRepository (source), Kustomization (apply), HelmRelease (chart with values), ImageRepository (image-based reconciliation).

→ Pattern: gitops

Investigate:

2.7 Node + workload autoscaling

Karpenter is a Kubernetes-native node autoscaler. It watches Pending pods, decides what nodes to launch, provisions them through cloud APIs (or local ones in homelab variants), reconciles continuously.

Keda is Kubernetes-Event-Driven Autoscaling. It scales workloads based on external signals, queue depth (Kafka, RabbitMQ), database metrics, custom Prometheus queries, not just CPU/memory.

Both follow the same CRD-driven controller pattern as the rest of the ecosystem.

Investigate:

2.8 Helm + Kustomize as unified packaging

Helm renders parameterized Kubernetes manifests from charts. Kustomize applies overlays (env-specific patches) on top of base manifests. Big-tech platform teams use both together: Helm for the chart (parameterized template), Kustomize for environment overlays (dev/staging/prod). Flux’s HelmRelease + Kustomization CRDs compose them naturally.

Investigate:


3. TRADE-OFFS

DecisionOptionsCost
DistributionK3s; kubeadm; managed (EKS/GKE)K3s: tiny, homelab. kubeadm: full vanilla. Managed: easy, opaque
GitOps toolFlux; ArgoCD; Argo WorkflowsFlux: K8s-native, compositional CRDs (recommended). ArgoCD: rich UI, more monolithic.
Stateful workload mgmtOperator (CloudNativePG, Redis Operator, etc.); raw StatefulSetOperator: reconciliation built in, recovery automated (recommended). Raw: more control, more ops burden.
Package mgmtHelm + Kustomize together; Helm alone; Kustomize alone; raw manifestsHelm + Kustomize: charts + overlays (recommended). Helm alone: parameterized but no env composition. Kustomize alone: composition but no templating.
Node autoscalingKarpenter; cluster-autoscaler; manualKarpenter: K8s-native, CRD-driven (recommended). cluster-autoscaler: legacy, config-driven.
Workload autoscalingHPA; Keda (event-driven)HPA: CPU/mem based. Keda: any external metric (recommended for queue/data workloads).
IngressCilium Gateway; Traefik; NGINXCilium Gateway: eBPF-native, composes with mesh. Traefik: K8s-native. NGINX: ubiquitous.
SecretsSealed-secrets; External Secrets Operator (Phase 27); SOPSESO: K8s-native, Vault-friendly (recommended; Phase 27 deepens)

4. TOOLS (as of 2026-06)

Core

Operators for stateful workloads

Autoscaling

Networking + storage

Reading


5. MASTERY: The basecamp substrate, K8s-native

5.1 What lives in basecamp by end of Phase 20

substrate  K3s              (running on the mini-PC)
         Flux             (bootstrapped via GitOps; reconciles basecamp repo)
         CloudNativePG    (operator-managed Postgres cluster)
         Redis Operator   (operator-managed Redis)
         Cert-manager     (TLS certs)
         Karpenter        (node autoscaling, mock provider in homelab)
         Keda             (event-driven workload autoscaling)
         Prometheus       (scraping pulse + node-exporter + kube-state-metrics)
         Grafana          (dashboards for pulse, Postgres, K8s)
module    beacon           (deployed via Flux HelmRelease + Kustomize overlay)

Helm charts + Kustomize overlays for env (dev/prod) are the standard packaging shape.

5.2 Operational depth checklist

[ ] Install K3s on a homelab VM (single-node initially)
[ ] Install Flux via Flux CLI; bootstrap GitOps reconciliation against basecamp repo
[ ] Deploy CloudNativePG operator; declare a Postgres `Cluster` CRD; verify HA + backups
[ ] Deploy Redis Operator; declare a Redis CRD; verify
[ ] Deploy cert-manager; issue a self-signed cert via Certificate CRD
[ ] Install Karpenter; verify it reconciles Pending pods to node provisioning
[ ] Install Keda; configure ScaledObject CRD for one workload based on queue depth or Prometheus metric
[ ] Deploy kube-prometheus-stack via Flux HelmRelease; verify metrics flow
[ ] Deploy pulse; verify Prometheus scrapes /metrics
[ ] Deploy Grafana dashboard for pulse
[ ] Implement NetworkPolicy: only beacon talks to Postgres Cluster
[ ] Ship beacon v0.1 to basecamp via Flux + Helm + Kustomize overlay; verify drift detection
[ ] Force Pod Pending; diagnose (resources? affinity? PVC?)
[ ] Force Deployment rollout failure; observe `kubectl rollout status`
[ ] Trigger etcd backup; restore from it
[ ] Practice operator-managed Postgres failover (CloudNativePG promotes a replica)

6. COMPARE: ArgoCD or Nomad

Pick one:

400-word reflection: what does each get right? Why did the K8s-native ecosystem converge?


7. OPERATE


8. CONTRIBUTE

Arc 3 first merged PR deadline: this phase. A docs PR counts.


What ships from this phase


Validation criteria

[ ] K3s + Flux operational; basecamp repo reconciled
[ ] CloudNativePG + Redis Operator managing stateful workloads
[ ] Karpenter + Keda operational
[ ] beacon v0.1 deployed via Flux HelmRelease + Kustomize overlay
[ ] All 15 operational depth checks
[ ] Compare reflection (400 words)
[ ] 8-10 K8s runbooks
[ ] 2-3 ADRs
[ ] First merged upstream PR
[ ] Pattern entries deepened:
    - control-loops → DEEP target
    - declarative-vs-imperative-infrastructure → OUTLINE
    - gitops → OUTLINE
    - load-balancing → OUTLINE
    - network-policy → OUTLINE
    - service-discovery → OUTLINE
    - mediation reinforced toward DEEP
[ ] Exit Test passed

Exit Test

Time: 4 hours.

Part 1: Build (120 min)

Fresh K3s cluster. Bootstrap Flux. Deploy a new small service via Flux HelmRelease + Kustomize overlay with: NetworkPolicy, Ingress with TLS (cert-manager), ServiceMonitor for Prometheus, Grafana dashboard. Stateful dependency via CloudNativePG Cluster. All declarative.

Part 2: Diagnose (90 min)

Three scenarios (Pod Pending, Ingress 502, Flux Kustomization stuck). Root-cause each + write runbooks.

Part 3: Articulate (60 min)

~1000 words: kubectl apply -f flux-bootstrap.yaml on a fresh cluster. Walk what happens end-to-end. Every controller that wakes up. Every CRD that gets reconciled. Every networking primitive that gets touched. Cite patterns at each layer. Then explain why CloudNativePG’s operator pattern beats a hand-rolled StatefulSet for production Postgres.”


Anti-patterns

Anti-patternWhy
Raw StatefulSets for production stateful workloadsOperator handles failover, backups, rolling upgrades; hand-rolled = pager calls
Helm without Kustomize for multi-envYou end up with three forks of the same chart
ArgoCD because tutorials are easierPick based on architecture fit, not tutorial count
Skipping NetworkPolicy “because homelab”Habits transfer to production
Memorizing kubectl instead of understanding the APIkubectl is a CLI over the API server; read API objects

Patterns touched this phase


→ Next: Phase 21: Distributed Systems Theory