Getting started

khook is a single static binary with the Kubernetes and Helm SDKs compiled in. It does not need kubectl, helm, or any other runtime dependency.

Install

With Go:

$ go install github.com/dvrkn/khook/cmd/khook@latest

Try it on a throwaway cluster

Create a local cluster with k3d and save this spec as quickstart.yaml:

# yaml-language-server: $schema=https://khook.io/schema/v1/khook.json
apiVersion: khook.io/v1
kind: Khook
metadata:
  name: quickstart
steps:
  - name: create-namespace
    apply:
      manifests:
        - inline: |
            apiVersion: v1
            kind: Namespace
            metadata:
              name: ingress
  - name: ingress-nginx
    needs: [create-namespace]
    helm:
      chart: ingress-nginx
      repo: https://kubernetes.github.io/ingress-nginx
      version: 4.8.3
      namespace: ingress
$ k3d cluster create dev
$ khook apply -f quickstart.yaml
✓ create-namespace (apply)  18ms
✓ ingress-nginx (helm)  21.457s

On a terminal, each step gets a live status line (pending, running, then ok/failed/skipped, with elapsed time). In CI, khook prints plain logs and a summary table, or JSON with --output json.

Run the command again: the namespace apply converges and the Helm release upgrades or no-ops. Re-runs are always safe.

Your first spec

A spec is a khook.io/v1 document with a list of steps. Each step has exactly one action key, and the key sets the step type:

# yaml-language-server: $schema=https://khook.io/schema/v1/khook.json
apiVersion: khook.io/v1
kind: Khook
metadata:
  name: bootstrap

defaults:          # fallbacks for every step
  timeout: 5m
  retries: 0
  onError: fail

steps:
  - name: cilium
    helm:
      chart: cilium
      repo: https://helm.cilium.io/
      version: 1.18.4
      namespace: kube-system
      atomic: true

  - name: all-ready
    needs: [cilium]        # runs only after cilium succeeds
    wait:
      for: condition=Ready
      on: pods
      allNamespaces: true

Steps are sorted topologically and run in parallel levels: every step whose needs are satisfied runs concurrently. A dependency cycle is a validation error.

The first line points yaml-language-server at the JSON Schema for editor validation and autocomplete.

Validate, plan, apply

$ khook validate -f bootstrap.yaml       # parse and validate; no cluster access
$ khook plan -f bootstrap.yaml           # read-only: install/upgrade/no-op per step
$ khook plan -f bootstrap.yaml --diff    # plus object diffs via server-side dry-run
$ khook apply -f bootstrap.yaml

plan never mutates the cluster; --offline skips cluster access entirely. graph prints the step DAG as Mermaid or Graphviz DOT. Exit code 2 means a validation error, 1 an execution failure. See the CLI reference.

Variables and secrets

${NAME} and ${NAME:-default} are resolved before parsing. Sources, highest precedence first: --set, --var-file, KHOOK_SECRET_* env vars, KHOOK_VAR_* env vars, in-spec defaults.

$ export KHOOK_VAR_ENV=prod
$ export KHOOK_SECRET_ECR_TOKEN="$(aws ecr get-login-password)"
$ khook apply -f bootstrap.yaml --set APP_NAME=payments

Only prefixed env vars are read, so PATH and unrelated CI secrets never reach a spec. KHOOK_SECRET_* values are also redacted from khook’s output. Values can go through sprig functions (${APP_NAME | lower | trunc 63}), and when: takes a CEL expression to make a step conditional. Details: DSL specification.

Next