Local Kubernetes Dev — Part 16: Conclusion and next steps
A recap of the whole series — what a fast, production-like local Kubernetes setup looks like — plus where to grow next: GitOps, observability, and remote-cluster tools.
Congratulations — if you've made it to this chapter, you have a real Kubernetes cluster running on your laptop, your myapp living inside it, PostgreSQL sitting right next to it, and code changes landing in the cluster within seconds. Let's sum up what we've built and talk about where to go next, once the basic local setup stops surprising you.
What we built: a recap
Let's retrace the whole journey. We assembled a local environment that behaves roughly the same way production does — what we called a production-like environment back at the start of the article (see chapter 2). Piece by piece, it looks like this:
- A local cluster. Lightweight Kubernetes via k3d (k3s inside Docker), named
dev, with themyappnamespace and a built-in image registry atk3d-registry.localhost:5000(see chapter 5). This isn't a Kubernetes "emulation" — it's a real cluster, just a small one. - A service image. A
Dockerfilefor Python 3.12 + FastAPI that builds into the same image that will ship to production (see chapter 6). - Real Kubernetes manifests.
Deployment,Service,Ingress— actual YAML objects, not adocker-compose.yml. This is the key idea of the whole article: locally you run exactly the same manifests as in production, so mistakes in them (a typo in aselector, a forgotten port, a brokenIngress) get caught on your laptop instead of after a rollout (see chapter 7). - A fast inner loop. Tilt rebuilds the image and updates the Pod every time you save a file, so the "change code → see the result" cycle (the very inner dev loop from chapter 1) takes seconds, not minutes (see chapter 8).
- Dependencies right alongside. PostgreSQL runs directly in the cluster, not "somewhere on staging" (see chapter 9).
- Configuration and secrets via
ConfigMapandSecret, not hardcoded (see chapter 10). - Health and resources.
livenessandreadinessprobes,requests/limitson CPU and memory — the things beginners usually forget and pay for painfully in production (see chapter 13). - Watching the cluster by eye through
kubectland k9s (see chapter 12).
The main value of all this is environment parity. You see problems with manifests, RBAC, network policies, and service discovery locally, instead of learning about them from alerts at three in the morning.
Checklist for a good local environment
Keep a short list handy. If every item is checked, your local setup really does resemble production rather than just pretending to.
- The same Kubernetes version and the same add-ons as in production. If production runs 1.30 with Ingress NGINX, let your local setup do the same. Version drift is a classic source of "well, it worked on my machine."
- Real manifests (Helm/Kustomize), not docker-compose. The same set of YAML for local and production, with values substituted via values files or overlays.
requestsandlimitson CPU/memory for every container. Without them a Pod can either starve or eat up the whole node. Locally, this also protects your laptop.- A
livenessprobe. Kubernetes restarts a hung container on its own. Formyappthis is a simpleGET /healthzthat only confirms "the process is alive" and doesn't reach into dependencies. See the probes documentation. - A
readinessprobe. The Pod is pulled out of theServiceendpoints until it's ready to take traffic — but without a restart. Formyappthis isGET /ready, which checks the connection to PostgreSQL (the difference between liveness and readiness is covered in detail in chapter 13). ConfigMap/Secretinstead of values in code. Config is separated from code — that's the idea of dev/prod parity from the 12-factor app.Ingressjust like production. If external traffic reaches production through an Ingress, route the same way locally so routing behavior matches (see chapter 11).- A fast inner loop. Save a file, see the result in seconds. If every cycle means a manual rebuild and
kubectl apply, you'll start avoiding the cluster — and that defeats the whole purpose.
This is a checklist about environment parity specifically. When it comes time for an actual rollout, also cross-check against the production-readiness checklist from chapter 13, which covers graceful shutdown, rolling updates, and QoS classes.
Next come three directions to grow in. These are no longer "required reading" for a beginner, but extensions worth adding as your project and team grow. Don't try to adopt everything at once.
Where to dig next: GitOps (ArgoCD/Flux)
While you're working solo and deploying via kubectl apply or Tilt, that's fine. But as the team and the number of environments grow, a question comes up: "what state is the cluster in right now, and who put it there?" The answer is GitOps: a Git repository becomes the single source of truth, and a dedicated controller in the cluster continuously reconciles the real state toward what's described in Git.
Two main tools, both with CNCF Graduated status (the highest maturity tier in the foundation):
- Argo CD — declarative GitOps CD. The controller compares the cluster's live state against what's in Git, flags differences as
OutOfSync, and synchronizes. It understands Helm, Kustomize, Jsonnet, and plain YAML. The headline feature for a beginner is its rich web UI, where an application (theApplicationCRD) shows up as a tree of objects with status highlighting. It supports multi-cluster, SSO, and RBAC. Argo was accepted into the CNCF and reached Graduated status. - Flux — this is the GitOps Toolkit, a set of controllers (source, kustomize, helm, notification). Out of the box it can do image automation (it updates the image tag in Git itself when a new build comes out) and work with OCI registries ("Gitless GitOps"). Flux is also CNCF Graduated. One caveat: Flux has no native web UI — for a dashboard you install third-party tools like Weave GitOps or Capacitor. So don't expect the "push-button" panel that Argo CD gives you out of the box.
A common pattern in practice: Argo CD for applications (a convenient UI for developers) and Flux for infrastructure. But this isn't dogma — pick one and start with it.
You can try Argo CD locally in your own dev cluster in a couple of commands:
1helm repo add argo https://argoproj.github.io/argo-helm
2helm install argocd argo/argo-cd -n argocd --create-namespace
3
4# access the ArgoCD web UI
5kubectl port-forward svc/argocd-server -n argocd 8080:443
6
7# register an application from your repository
8argocd app create myapp \
9 --repo https://github.com/<user>/<repo>.git \
10 --path k8s \
11 --dest-server https://kubernetes.default.svc \
12 --dest-namespace myappWith Flux, you usually get started by bootstrapping straight into Git:
1flux bootstrap github \
2 --owner=<user> \
3 --repository=<repo> \
4 --path=clusters/devObservability stack (Prometheus, Grafana, OpenTelemetry)
In chapter 12 we looked at the cluster "by hand" — through kubectl logs and k9s — and that's where we introduced the three pillars of observability (metrics, logs, traces). The next level is to collect them systematically instead of eyeballing them.
The fastest way to get a complete metrics stack is kube-prometheus-stack, a Helm chart from the prometheus-community. With a single command it installs the Prometheus Operator, Prometheus itself, Alertmanager, Grafana, kube-state-metrics, and node-exporter — plus ready-made dashboards and alerting rules:
1helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
2helm install kps prometheus-community/kube-prometheus-stack \
3 -n monitoring --create-namespace
4
5# Grafana (default login admin / prom-operator)
6kubectl port-forward svc/kps-grafana -n monitoring 3000:80An important caveat: this stack is heavy. Prometheus, Grafana, and the operators together can take down a weak k3d cluster. On a laptop with limited memory, disable the components you don't need in the values file or constrain requests, otherwise the cluster will start choking.
For Prometheus to scrape the metrics of your myapp specifically, you don't need to edit its config by hand. The Prometheus Operator introduces the ServiceMonitor and PodMonitor CRDs: you simply describe which Service to scrape, via label selectors, without any Prometheus-specific language:
1apiVersion: monitoring.coreos.com/v1
2kind: ServiceMonitor
3metadata:
4 name: myapp
5 namespace: myapp
6 labels:
7 release: kps # so that our Prometheus picks up this monitor
8spec:
9 selector:
10 matchLabels:
11 app: myapp
12 endpoints:
13 - port: http # the port name from the Service
14 path: /metrics
15 interval: 15s(For this to work, myapp has to expose metrics in Prometheus format — for example, via the prometheus-fastapi-instrumentator library on the /metrics endpoint.)
Metrics are half of observability. The other half is traces and logs, and here the industry is converging on OpenTelemetry: a vendor-neutral observability framework under the CNCF umbrella with three signals — traces, metrics, logs. It has an SDK (with auto-instrumentation for popular frameworks, including FastAPI) and a Collector that receives, processes, and exports telemetry anywhere. A typical evolution path: you deploy an OpenTelemetry Collector, send metrics to Prometheus, traces to Tempo, logs to Loki, and view all of it in a single Grafana. The beauty of the approach is that the same stack scales from a local k3d up to large-scale production.
Connecting to a remote cluster (Telepresence/mirrord) for the hard cases
We mentioned these tools briefly in the overview in chapter 3; here's a short recap, with an emphasis on when to reach for them at all.
A local cluster covers most tasks. But there are "hard cases": a service drags along a dozen dependencies that are expensive or impossible to spin up on a laptop, or you need to debug your code against the data and neighbors of a real (staging) cluster. That's where tools that connect your local process to a remote cluster come in handy.
- Telepresence (open source) — sets up a "remote dev environment": you install a client on your machine and a traffic manager in the cluster, after which your local code, IDE, and debugger work as if they were running inside the cluster. Its intercept mechanism redirects the chosen service's traffic to your local port. It works on a VPN/tun model and therefore requires root privileges on the machine plus installing a component into the cluster — a powerful path, but not the lightest.
1telepresence helm install 2telepresence connect 3telepresence intercept myapp --port 8080:80 - mirrord — runs your local binary as if it lived inside a Pod in the cluster, but without root and without containerization. It injects a
mirrord-layerinto your process and spins up a temporarymirrord-agentnext to the target Pod. The key nuance is the incoming-traffic modes:mirror(the default) — your process receives a copy of the incoming traffic. Safe for shared dev clusters: you see the requests but don't take them away from others.steal— your process intercepts the Pod's incoming traffic. Handy for debugging, but in a shared cluster you'll divert other people's requests to yourself — be careful.
1# mirror mode (safe for a shared environment) 2mirrord exec --target pod/<pod> -- uvicorn app.main:app --port 8080 3 4# steal mode (intercepts incoming traffic) 5mirrord exec --target deployment/myapp --steal -- uvicorn app.main:app --port 8080
A comparison of how these tools work is covered well on the Kubernetes blog. The main point: reach for them only when a local cluster genuinely can't handle the scenario. For day-to-day development of myapp, your k3d local setup with Tilt is faster and simpler.
Useful links and resources
In short, here's what I'd keep bookmarked after reading this article:
- GitOps: Argo CD and Flux.
- Metrics and dashboards: kube-prometheus-stack and Prometheus Operator.
- Traces/logs/metrics under a single standard: OpenTelemetry.
- Remote debugging: Telepresence and mirrord.
And with that, the loop closes. You know how to spin up a cluster, package a service, write manifests, speed up development, and debug problems — and you know where to grow when the project outgrows your laptop. Good luck with myapp in production. Come back to any chapter whenever you need to refresh it — start again from the inner dev loop.
Sources
- Argo CD — Declarative GitOps CD for Kubernetes (official docs)
- Argo — CNCF project page
- Flux — Core Concepts (official docs)
- Flux — CNCF project page
- kube-prometheus-stack Helm chart (prometheus-community)
- Prometheus Operator — Introduction (official docs)
- What is OpenTelemetry? (official docs)
- Telepresence — Quick Start (official docs)
- mirrord documentation (MetalBear)
- Comparing Local Kubernetes Development Tools: Telepresence, Gefyra, and mirrord (Kubernetes blog)
- Kubernetes — Configure Liveness, Readiness and Startup Probes
- The Twelve-Factor App — Dev/prod parity