Lesson 6 · This repo's local dev

The two local stacks

docker-compose vs kind + Skaffold — why this repo has both, and the surprising thing they share.

Your win: know the two ways to run the whole backend locally, when to reach for each, and the non-obvious fact that the compose stack's config is generated from the Skaffold stack.

In plain English Plain English: the repo gives you a fast container-only stack and a slower but more production-like Kubernetes stack, and they share configuration truth.

Two stacks, one backend

You can bring the entire backend up on your laptop in two completely different runtimes:

docker-composekind + Skaffold
Entry point./local/run.bash./deployments/sk.bash
RuntimeDocker Compose (Lesson 4)a real Kubernetes cluster
Clusternone — just containerskind (K8s-in-Docker)
Deploys viadocker compose upSkaffold → Helm → kubectl
Speedfast, lightslower, heavier
Fidelity"just the processes"production-like (namespaces, Services, Istio)
Namespacen/alocal-manabie-backend (Lesson 5)
When to use which Use docker-compose for day-to-day service work — it's the fast inner loop, and it's what you already run. Use kind + Skaffold when you need the parts that only exist in Kubernetes: how a Deployment/Service behaves, Helm rendering, Istio routing, KEDA autoscaling. Interview framing: "compose for speed, kind for fidelity."

Stack 1 — docker-compose (run.bash)

What Part 1 described: infra + service + migration Compose files, a wait-for-it-service healthcheck gate, ~25 services all on local-manabie-network. Bring it all up with a bare ./local/run.bash (local/run.bash:107-170) — it builds the image, generates configs, creates the shared volume/network, and runs compose-up-all.bash.

Stack 2 — kind + Skaffold (sk.bash)

This one spins up an actual Kubernetes cluster inside Docker containers using kind, then uses Skaffold to build the images and deploy the Helm charts into it.

Anchor — how sk.bash boots a cluster A plain ./deployments/sk.bash ends in a skaffold run, and just before it, start_cluster() (deployments/sk.bash:126-129) calls deployments/kind_with_registry.bash — kind plus a local image registry. It deploys with flags --default-repo=asia.gcr.io/student-coach-e1e95 --skip-tests --status-check=false against kube-context kind-kind, namespace local-manabie-backend (deployments/sk.bash:14-15). -d deletes the whole cluster.

The surprise: the stacks aren't really independent

Compose configs are rendered from Skaffold/Helm You might assume the two stacks keep separate config. They don't. When run.bash starts everything, build_config_and_secrets() (local/run.bash:22-35) shells out to the Skaffold stack to produce the compose configs:
./deployments/sk.bash -- render -f skaffold.backend.yaml -p local-docker-compose,-local --output ./local/configs/backend.yaml
So Helm (via Skaffold render, Lesson 7) is the single source of truth for configuration; the compose files just consume its output. Change a Helm value and both stacks see it. This is why the two aren't allowed to drift.
Helm charts + {env}-{org}-values.yaml (single source of config truth) │ skaffold render ┌─────────────────────────────┐ ├───────────►│ Stack 1: docker-compose │ ← run.bash (fast) │ └─────────────────────────────┘ │ ┌─────────────────────────────┐ └───────────►│ Stack 2: kind + Skaffold run │ ← sk.bash (production-like) └─────────────────────────────┘
Read this next

kind — Kubernetes IN Docker

What kind is and how it runs a real cluster inside containers — the foundation of Stack 2 and a running theme in Course 2 (Kubernetes).

kind.sigs.k8s.io
→ In-repo: local/run.bash:22-35 · deployments/sk.bash:126-129

Common mistake Common mistake: assuming the two stacks are independent implementations when the compose side is actually rendered from the Helm/Skaffold side.

Check yourself (from memory)

Q1. The kind + Skaffold stack differs from compose in that it…

kind = Kubernetes-in-Docker; Skaffold builds + deploys the Helm charts into it. Production-like, at the cost of speed.

Q2. Where do the docker-compose stack's configs come from?

run.bash calls sk.bash -- render; Helm is the single config source, so the stacks can't drift.

Q3. For fast day-to-day service iteration you'd reach for…

Compose is the fast inner loop; save kind for testing K8s/Helm/Istio-specific behaviour.
Two local stacks — name them, when to use each, and what they share.
recall, then click to reveal
STACK 1 — docker-compose via ./local/run.bash: plain containers, FAST, day-to-day service dev. STACK 2 — kind + Skaffold via ./deployments/sk.bash: a REAL Kubernetes cluster (kind = K8s-in-Docker) with Skaffold building + deploying Helm charts into namespace local-manabie-backend — SLOWER but production-like (Services, Istio, KEDA). Rule: "compose for speed, kind for fidelity." SHARED: the compose configs are RENDERED from Helm via skaffold render (run.bash:22-35) — Helm is the single config source, so the two stacks can't drift.
Want to know what kind_with_registry.bash sets up, or why a local registry is needed? Ask me.

1. kind — Kubernetes in Docker; in-repo local/run.bash:22-35,107-170, deployments/sk.bash:14-15,126-129.