kubernetes automation · tekton ci · v2.0 · mit

Your cluster. Your pipelines. No lock-in.

kubementat takes a bare Kubernetes cluster and one config directory to a productive one: kmt applies a declared set of open-source releases — Tekton CI, Prometheus & Grafana, Loki & Alloy, Traefik with cert-manager, OpenBao, Polaris — and gives you pipelines for building and shipping your own apps on top.

bare cluster→ 1 config dir→ 1 command→ productive cluster

helm releases
0
kmt commands
0
config dirs
0
vendor lock-in
0
operator@lab: ~

A cluster is only productive
when it already has everything.

Do it by handkubementat
First productive dayweeks of choosing, installing, wiringone apply, converged
Monitoring, logging, routing, secrets, compliancefive projects wearing a trench coatdeclared in one helmfile, applied by kmt
CI/CD on Kubernetesa second weekend with JenkinsTekton tasks, pipelines & webhook triggers, generated per app
Cluster versionswhatever the docs mentioned lasta release index with a version window — 2.x answers today
Where it runstied to one providerlaptop, Raspberry Pi, on-prem, any cloud
Your configurationspread across manifests and tribal memoryone git-managed directory, encrypted where credentials live

No service mesh. No nginx-ingress. kubementat installs neither, on purpose. Linkerd is swept from a cluster that really has it, and nginx-ingress left on 2026-10-28 — Traefik with cert-manager in front does the routing. Convention over configuration means the choices are already made, so your first hour is spent on your apps, not on the platform.

The config directory is the product.

  • ~/kmt-config/

    Named, never located. --config-dir or $KUBEMENTAT_PLATFORM_CONFIG_DIR — nothing is discovered by proximity. It is a git repository in its own right, prepared by kmt config init, not a subdirectory of a checkout.

  • environment / team layers

    Every key answered once. The team file, else the environment file, else a shipped default. kmt config show <env> prints each key and who answered it.

  • helmfile inside kmt

    Releases are configuration. What gets applied is declared in the helmfile that ships inside kmt; versions come from its release index. Both are answered over by your tree.

  • all Python, no scripts

    One implementation. Every verb — user management, secrets, tunnels, component applies — is Python in the library, imported and never exec'd. There is no scripts/ to fall behind the docs.

~/kmt-config/               # prepared by `kmt config init`
├── kubementat.yaml             # the stamp: product + schema
├── .gitattributes              # *.encrypted.* filter=git-crypt
├── deployer_ssh_key{,.pub}     # the deploy keypair
├── git_crypt_symmetric.key     # mode 0600 — cannot be regenerated
└── dev/
    ├── static.json             # environment layer: what this cluster is
    ├── static.encrypted.json   # credentials, under git-crypt
    ├── tekton/{tasks,pipelines}
    └── dev1/
        ├── static.json         # team layer — answers over the env
        └── tekton/…            # tasks the team owns replace the rest
config directorystatic.json · encrypted layers · tekton/ release indexthe helmfile that ships inside kmt kmt installdependency check → helmfile apply → tekton 10 helm releasesmonitoring · logging · routing · secrets Tekton tasks & pipelinestriggers · resolvers · dashboard Traefik :443 + cert-manager — every service your cluster runs, served and secured
Your tree answers over shipped defaults — reproducible on every cluster you set up next.

A kubementat environment is defined by that directory, not by where the tool was installed. Since v2 the checkout is irrelevant to what your clusters do:

  • Git-Crypt where it counts — credentials live in *.encrypted.* files inside your tree; kubementat reads them and never unlocks anything itself.
  • Refusals are sentences — a missing binary, an unnamed kube context or an unknown key comes back as the name of what was missing, not a traceback.
  • Idempotent by design — config init writes no byte on a second run and names what it skipped; applies converge.

Install, name it, apply.

  1. 01

    Install the kmt CLI

    uv tool install kubementat      # or: pipx install kubementat
    kmt --help

    From a checkout (you are hacking on kubementat) it is uv sync --all-extras, then every command is uv run kmt …. For operating clusters the tool install is all you need — the configuration directory is independent of the tree kubementat came from.

  2. 02

    Prepare the configuration directory

    kmt config init --environment dev --team dev1 \
        --gpg-email you@example.com --config-dir ~/kmt-config

    Writes the environment's and the team's static layers, the deploy keypair, and git-crypt made live in that directory — in that order. --set KEY=VALUE answers plain keys, --encrypted-set the protected ones. It commits its own work once; a second run converges without touching a byte.

  3. 03

    Name the cluster

    kubectl config get-contexts
    kmt config set dev CLUSTER_KUBE_CONTEXT=dev-cluster --config-dir ~/kmt-config
    kmt environments list

    A new environment answers CLUSTER_KUBE_CONTEXT as null and install refuses until you name one — copying whatever kubectl was pointed at would be a claim the tool did not earn. Nothing is resolved from current-context.

  4. 04

    Apply everything

    kmt install
    kmt setup-secrets

    kmt install checks the machine first — kubectl, helm, helmfile, helm-diff, the tkn CLI, gpg, git — each missing binary named in its own sentence. Then it applies the helmfile, wires Tekton, and your cluster is productive. setup-secrets lays the team's credentials so pipelines can pull and push.

Lose the symmetric key and you lose the environment. git_crypt_symmetric.key cannot be regenerated and nothing can rotate it. init says so while writing it, mode 0600, gitignored — back it up like the cluster itself.

One binary, grouped verbs.
Everything refuses in words.

The same 14 commands whether you are on a laptop or in a pipeline. Nine of them are groups that each answer --help with the refusals they make; five act on a whole cluster.

kmt config …

init, set / unset, show, validate, add-environment, add-team — the verbs that lay down and read a configuration tree

kmt environments list

one row per environment: its address and the kube context that answers for it

kmt kubernetes …

what runs in the cluster: analyze-deployment, deployment-logs / -ssh, pod-images, pvc-usage, cluster-capacity, create-ingress, service-account-token

kmt helm …

apply the environment's helmfile; latest-version asks the release index what is new

kmt tekton …

setup-pipelines, setup-triggers, run-pipeline, generate-deploy-pipeline / -pipeline-run / -triggers, list, cleanup, uninstall

kmt component …

install or uninstall a single release — helmfile scoped to one name; the namespaces kubementat creates are refused

kmt routing …

install applies Traefik + cert-manager; status reads which Traefik the cluster is actually running, and whose certs

kmt tunnel …

grafana, openbao-ui, tekton, or any pod — a port-forward without the kubectl invocation ceremony

kmt users …

team accounts with RBAC and tokens, read-only variants, Grafana accounts, and the clean delete that takes the token with it

install · uninstall

the whole platform onto a cluster, or off of it, with the dependency check up front

setup-secrets · setup-docker-registry-access

team credentials, and the pull secrets an answered DOCKER_REGISTRY_BASE_URL needs

run-ci-container

the automation container, from the same image the pipelines run in

# the machine-readable view of one environment
kmt config show dev --config-dir ~/kmt-config

# before anything touches the cluster
kmt config validate --config-dir ~/kmt-config

# one release at a time, converged
kmt component install grafana
kmt routing status
every verb answers the same way
✓converged — re-running writes nothing
✗named refusal: missing binary, null key, unknown component
·skipped, and the sentence says why

Ten releases, declared,
version-pinned.

What a run applies is what the helmfile says — and that helmfile ships inside kmt itself. The versions come from its release index; the version window answers for 2.x. Every component is upstream, unmodified, and replaceable.

  • Tektonci/cd

    pipelines

    Pipelines, Triggers, the Dashboard and the resolvers — installed by kmt tekton setup-pipelines, not by the helmfile.

    tekton.dev
  • Helm · Helmfiledelivery

    how every release arrives

    The desired state of a cluster is one declarative file. Diff, apply, converge — and helm diff tells you what changed before it does.

    helmfile.readthedocs.io
  • Traefikrelease

    routing

    The ingress class kubementat installs, applied by kmt routing install. kmt routing status reads which Traefik is really running.

    traefik.io
  • cert-managerrelease

    certificates

    Lives "in front of" Traefik in the same group: real certificates for every hostname your cluster serves, renewed before they expire.

    cert-manager.io
  • Prometheusrelease

    metrics

    The metrics core, beside the blackbox exporter for probes and the helm exporter so released state is a metric like any other.

    prometheus.io
  • Grafanarelease

    dashboards

    Where the cluster is read. kmt users create-grafana-accounts wires the team accounts in; kmt tunnel grafana shows it to you.

    grafana.com
  • Loki · Alloyrelease

    logs

    Log aggregation and analysis as a pair: Alloy collects and fans out, Loki is the store you query. No ELK sprawl.

    grafana.com/oss/loki
  • OpenBaorelease

    secrets

    The cluster's secret store, forked from Vault before the license changed. The place secrets live when an annotation is not enough.

    openbao.org
  • Polarisrelease

    compliance

    Configuration best-practice scanning across the cluster — so drift in your own workloads gets caught by the platform, not by an incident.

    fairwinds.com/polaris
  • Examplesrepo

    your apps

    Worked examples: an nginx chart deployed by generated Tekton pipelines, manifests, and the tasks that build and ship an app on Kubernetes.

    examples/

Deliberately absent. No service mesh — Linkerd is swept from a cluster that really has it and installed by nothing. No nginx-ingress. And cluster-cronjobs, goldilocks and vpa are namespaces kubementat creates, not releases it applies — kmt component install refuses them for exactly that reason.