Skip to content

202511082153 Turborepo + Kustomize + Kubectl ❤️

After migrating the cluster to FluxCD 202510041935 Deploying Via FluxCD, I’ve noticed that one thing I missed alot were the diffs that tanka provided OOB.

The solution was simple, run kustomize alongside kubectl diff to stdout or output it into a file. So far, I’ve opted to execute it manually to bash scripts to ensure the generated YAMLs are valid.

After merging the kubernetes subdirectory into forge and intending to leverage it as a 202511071816 Turborepo As A Multi-language Monorepo Orchestrator , it became clear that I could use turbo scripts on top to orchestrate this diff process; locally and through the CI.

With turborepo we can also achieve environment parity by the choice of tooling. This fundamentally reduces overhead. What if we could one or two simple command that could orchestrate the scripts? With package.json as a way to declare projects in the monorepo, Turborepo can use it as entrypoints into the project.

Take this package.json as an example

{
"name": "@syahshiimi/kubernetes",
"version": "1.0.0",
"scripts": {
"k8s:diff:apps": "kustomize build apps/ | kubectl diff -f - || true",
"k8s:diff:apps:ci": "kustomize build apps/ | kubectl diff -f - > app.diff.txt || true",
"k8s:diff:infra": "kustomize build infrastructure/ | kubectl diff -f - || true",
"k8s:diff:infra:ci": "kustomize build infrastructure/ | kubectl diff -f - > infra.diff.txt || true"
},
"repository": {
"type": "git",
"url": "https://github.com/syahshiimi/homelab.git"
},
"private": true
}

scripts.k8s:diff:apps is merely a an executable set of commands that pnpm (my choice of package manager and node executable) could use. It executes the kustomize build step, followed by a kubectl diff against a cluster.

A variation of this script exists, though suffixed with :ci could also ran locally. The CI choice is different - the goal is to output the diff for GH issue comments.

All of this is declared and defined in the root turbo.jsonc which is the declarative way to define behaviour(s) in the repo.

...
"k8s:diff:apps": {
"cache": true,
"persistent": false
},
"k8s:diff:apps:ci": {
"cache": true,
"persistent": false
},
"k8s:diff:infra": {
"cache": false,
"persistent": false
},
"k8s:diff:infra:ci": {
"cache": false,
"persistent": false
}
}}

And declared against, in the root package.json to the behaviours/commands to be executed from root.

{
"name": "forge",
"private": true,
"scripts": {
"k8s:diff:apps": "turbo run k8s:diff:apps",
"k8s:diff:infra": "turbo run k8s:diff:infra",
"k8s:diff:ci": "turbo run k8s:diff:apps:ci k8s:diff:infra:ci",
"k8s:diff:apps:ci": "turbo run k8s:diff:apps:ci",
"k8s:diff:infra:ci": "turbo run k8s:diff:infra:ci",
},
"devDependencies": {
"prettier": "^3.6.2",
"turbo": "^2.6.0",
"typescript": "5.9.2"
},
"packageManager": "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd",
"engines": {
"node": ">=22"
}
}

Executing pnpm k8s:diff:ci locally would then output two files in the kubernetes submodule /kubernetes as both app.diff.txt and infra.diff.txt.

Nice!