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.
Two stacks, one backend
You can bring the entire backend up on your laptop in two completely different runtimes:
| docker-compose | kind + Skaffold | |
|---|---|---|
| Entry point | ./local/run.bash | ./deployments/sk.bash |
| Runtime | Docker Compose (Lesson 4) | a real Kubernetes cluster |
| Cluster | none — just containers | kind (K8s-in-Docker) |
| Deploys via | docker compose up | Skaffold → Helm → kubectl |
| Speed | fast, light | slower, heavier |
| Fidelity | "just the processes" | production-like (namespaces, Services, Istio) |
| Namespace | n/a | local-manabie-backend (Lesson 5) |
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.
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
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.
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
Check yourself (from memory)
Q1. The kind + Skaffold stack differs from compose in that it…
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…
./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.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.