Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

14 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

kindctl

CI

kindctl is a small Bash wrapper and agent skill for running repo/worktree-scoped kind clusters without ever touching your global Kubernetes config.

It lets you create and operate many local kind clusters across many repos and git worktrees while keeping each cluster isolated in its own kubeconfig:

~/.kube/kind/<derived-cluster-name>.kubeconfig

The global kubeconfig stays out of the loop:

~/.kube/config  # never read or written by kindctl

Motivation: agents across many repos and worktrees

AI coding agents often work differently from humans:

  • they jump between repos quickly;
  • they run commands from fresh shell invocations;
  • they may work in several git worktrees of the same repo;
  • they can forget implicit shell state like export KUBECONFIG=...;
  • they should never accidentally point kubectl at the wrong local cluster.

Raw kind is not ideal for that workflow. A normal command like this:

kind create cluster --name my-service

writes a context into ~/.kube/config and switches the active Kubernetes context. With multiple agents, repos, and worktrees, that turns your global kubeconfig into shared mutable state. One terminal or agent can silently change which cluster another command targets.

kindctl avoids that by making the cluster identity deterministic from the current workspace path:

repo/worktree path -> hash -> cluster name -> scoped kubeconfig

For example:

/Users/me/projects/api              -> api-a13f02
/Users/me/projects/api-feature-wt   -> api-feature-wt-89be11
/Users/me/projects/web              -> web-7710aa

Each worktree gets its own cluster automatically. Re-running kindctl from the same worktree reuses the same derived cluster and kubeconfig. Running it from another worktree derives a different cluster.

That gives agents a simple rule:

When working with local kind clusters, use kindctl kubectl or kindctl exec, never bare kubectl.

No global context switching. No stale kind-* contexts in ~/.kube/config. No accidental cross-repo cluster targeting.

How it works

From any repo or worktree, kindctl derives:

workspace root: git root, or nearest .kind/ marker, or cwd
hash:           first 6 chars of sha256(workspace root)
cluster name:   <sanitized basename>-<hash>[-tag]
context:        kind-<cluster name>
kubeconfig:     ~/.kube/kind/<cluster name>.kubeconfig

Example:

root:       /Users/me/projects/my-api
cluster:    my-api-2440c3
context:    kind-my-api-2440c3
kubeconfig: ~/.kube/kind/my-api-2440c3.kubeconfig

kindctl create calls kind with an explicit scoped kubeconfig:

kind create cluster \
  --name my-api-2440c3 \
  --kubeconfig ~/.kube/kind/my-api-2440c3.kubeconfig

All later operations derive the same name again from the current directory.

Install

Install the skill globally with the standard skills installer:

npx --yes skills@latest add sozercan/kindctl \
  --global \
  --skill kindctl \
  --agent claude-code \
  --agent codex \
  --yes

From a local checkout, the Makefile delegates to the same installer:

make install-skill

The skills CLI owns the agent-specific paths, including the universal ~/.agents/skills layout and any Claude/Codex wiring it needs. This avoids hard-coding every supported agent's install directory in this repo.

Optional CLI convenience:

make install-cli

That symlinks only the executable:

~/.local/bin/kindctl -> this repo/bin/kindctl

You can also call the wrapper directly:

/Users/sozercan/projects/kindctl/bin/kindctl --help

Quick start

Create a cluster for the current repo/worktree:

kindctl create

Use it safely:

kindctl kubectl get nodes
kindctl kubectl get pods -A

Run other tools with scoped KUBECONFIG:

kindctl exec -- helm list -A
kindctl exec -- k9s

Delete it:

kindctl delete

Multiple worktrees

Two git worktrees of the same repo automatically get different clusters because their absolute paths differ.

cd ~/projects/my-api
kindctl create
kindctl path

cd ~/projects/my-api-feature-worktree
kindctl create
kindctl path

The two path outputs will point at different files under ~/.kube/kind/.

Running from subdirectories inside the same git worktree reuses the same cluster because kindctl normalizes to the git root.

Multiple clusters per repo

Use --tag when one repo needs more than one cluster:

kindctl create --tag e2e
kindctl kubectl --tag e2e get nodes
kindctl delete --tag e2e

Tags are sanitized and capped so kind cluster names remain within kind/hostname limits.

Repo-specific configuration

A repo can optionally commit:

.kind/
  cluster.yaml
  setup.sh

.kind/cluster.yaml

Native kind config. Use it for Kubernetes version, node count, port mappings, mounts, and networking.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
  - role: worker

kindctl passes this file to kind but still injects the derived cluster name with --name.

.kind/setup.sh

Optional executable post-create hook. It runs with scoped environment:

KUBECONFIG=<scoped kubeconfig>
KINDCTL_CLUSTER=<cluster name>
KINDCTL_CONTEXT=kind-<cluster name>
KINDCTL_ROOT=<workspace root>

Example:

#!/usr/bin/env bash
set -euo pipefail

kubectl apply -f config/crd/bases
kubectl apply -k config/default

Inside the hook, bare kubectl is safe because KUBECONFIG is scoped for that process.

Commands

kindctl create [--config FILE] [--tag TAG] [--k8s-version vX.Y.Z]
kindctl delete [--tag TAG]
kindctl exec [--tag TAG] -- <command> [args...]
kindctl kubectl [--tag TAG] <args...>
kindctl path [--tag TAG]
kindctl ctx [--tag TAG]
kindctl env [--tag TAG]
kindctl list [--workspace|--all]
kindctl load [--tag TAG] <image>
kindctl hibernate [--tag TAG]
kindctl resume [--tag TAG]
kindctl doctor
kindctl prune (--workspace|--dead) [--yes]
kindctl nuke [--yes]

Safety properties

  • No arbitrary --name override in v1.
  • No code path reads or writes ~/.kube/config.
  • Each kubeconfig is stored under ~/.kube/kind/.
  • Store directory is 0700; registry and kubeconfigs are 0600.
  • Registry writes use a lock and atomic replace.
  • Bulk destructive operations are registry-scoped.
  • Unmanaged kind clusters are reported but not deleted.
  • hibernate/resume select containers by kind's Docker label, not by name glob.

GitHub Actions security and dependency updates

Workflow actions are pinned to full commit SHAs, and make lint includes a workflow pinning check so unpinned uses: references fail CI. Dependabot is configured for GitHub Actions updates, and a Dependabot automerge workflow merges successful non-draft Dependabot PRs after the main CI workflow passes.

Testing

Fast mocked tests:

make lint
make test

Real kind/docker integration tests:

make test-integration

The integration test creates real clusters for multiple worktrees, verifies unique cluster names, verifies reuse within each worktree, and deletes the clusters afterward.

Current invariant

No kindctl code path β€” and no tool the skill tells an agent to run β€” ever reads or writes ~/.kube/config. Every cluster lives in its own ~/.kube/kind/<name>.kubeconfig, addressed by a deterministic worktree-path hash plus optional sanitized tag.

About

πŸ§‘β€πŸ”§ Agent-friendly kind cluster manager for per-repo/worktree local Kubernetes without global kubeconfig pollution

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages