Skip to content

Installation

This guide covers installing kardinal-promoter in a Kubernetes cluster using Helm.


Prerequisites

Requirement Version Notes
Kubernetes ≥ 1.28 kind, EKS, GKE, or any conformant cluster
kubectl ≥ 1.28 Matches your cluster version
Helm ≥ 3.12 brew install helm
GitHub token Personal access token with repo scope

Trying it out locally?

Use kind for a single-node local cluster. See the Quickstart for the fast path.

krocodile is bundled

kardinal-promoter bundles the krocodile Graph controller as part of its Helm chart. No separate krocodile install is required. A single helm install installs both controllers.


Install kardinal-promoter

1. Create the GitHub token secret

kardinal-promoter needs a GitHub personal access token to open and monitor pull requests. The token requires the repo scope (contents read/write, pull requests read/write).

kubectl create namespace kardinal-system

kubectl create secret generic github-token \
  --namespace kardinal-system \
  --from-literal=token=<your-github-token>

2. Install with Helm

helm install kardinal-promoter oci://ghcr.io/pnz1990/charts/kardinal-promoter \
  --namespace kardinal-system \
  --create-namespace \
  --set github.secretRef.name=github-token

This single command installs:

  • The kardinal-promoter controller in the kardinal-system namespace
  • The krocodile Graph controller in the kro-system namespace (unless --set krocodile.enabled=false)

Verify both controllers are running:

kubectl get pods -n kardinal-system
# NAME                                              READY   STATUS    RESTARTS   AGE
# kardinal-promoter-controller-6b6c8c8446-jc79g     1/1     Running   0          30s

kubectl get pods -n kro-system
# NAME                              READY   STATUS    RESTARTS   AGE
# graph-controller-7d4b8f9f5-xk2pq  1/1     Running   0          30s

Bring your own krocodile

If you already run krocodile independently in your cluster, disable the bundled installation:

helm install kardinal-promoter oci://ghcr.io/pnz1990/charts/kardinal-promoter \
  --namespace kardinal-system \
  --create-namespace \
  --set github.secretRef.name=github-token \
  --set krocodile.enabled=false

Version compatibility

When running your own krocodile, ensure it is at a compatible commit. The required minimum is documented in hack/install-krocodile.sh. The bundled version is pinned per release and tested together.


Helm values reference

kardinal-promoter controller

Key Default Description
replicaCount 1 Number of controller replicas
image.repository ghcr.io/pnz1990/kardinal-promoter/controller Controller image
image.tag Chart appVersion Image tag
image.pullPolicy IfNotPresent Pull policy
logLevel info Log verbosity (debug, info, warn, error)
leaderElect true Enable leader election (required for HA)
github.token "" Direct token value (use secretRef in production)
github.secretRef.name "" Name of existing Secret containing the token
github.secretRef.key token Key in the Secret
resources.limits.cpu 500m CPU limit
resources.limits.memory 128Mi Memory limit
resources.requests.cpu 10m CPU request
resources.requests.memory 64Mi Memory request
nodeSelector {} Node selector
tolerations [] Pod tolerations
affinity {} Pod affinity
validatingAdmissionPolicy.enabled true Deploy ValidatingAdmissionPolicy (requires Kubernetes ≥ 1.28)

krocodile Graph controller

Key Default Description
krocodile.enabled true Install bundled krocodile controller
krocodile.image.repository ghcr.io/pnz1990/kardinal-promoter/krocodile krocodile image
krocodile.image.tag krocodile.pinnedCommit Image tag (defaults to pinned commit SHA)
krocodile.pinnedCommit See Chart.yaml annotations Source commit bundled with this release
krocodile.namespace kro-system Namespace for krocodile controller
krocodile.replicaCount 1 Number of krocodile replicas
krocodile.resources.limits.memory 512Mi Memory limit

Accessing the UI

The kardinal controller serves an embedded web UI at port 8082 (configurable via --ui-listen-address).

The supported access method for in-cluster deployments without Ingress is kubectl port-forward:

kubectl port-forward svc/kardinal-controller -n kardinal-system 8082:8082

Then open http://localhost:8082/ui/ in your browser.

Why port-forward?

Port-forwarding routes traffic through the Kubernetes API server over a secure tunnel — no Ingress or LoadBalancer needed. It is the recommended approach for platform engineers accessing the UI from their workstation.

Avoid accessing the UI over plain HTTP from a remote address

If you expose port 8082 directly (e.g. via NodePort) without TLS, the UI will display a security warning. Use port-forward from localhost instead, or configure TLS with --tls-cert-file / --tls-key-file.

With TLS (production)

If you configure TLS via --tls-cert-file / --tls-key-file (or the Helm values controller.tlsCertFile / controller.tlsKeyFile), the UI is served over HTTPS and no browser warning is shown. See the Security guide for a cert-manager example.


Upgrade

helm upgrade kardinal-promoter oci://ghcr.io/pnz1990/charts/kardinal-promoter \
  --namespace kardinal-system \
  --reuse-values

Graceful shutdown

The controller handles SIGTERM gracefully: it stops accepting new reconcile requests and allows in-flight reconcile loops up to 30 seconds to complete before exiting. This prevents mid-step interruptions during rolling updates or node evictions — for example, a git-push that was 5 seconds from finishing will complete rather than leaving the PromotionStep in an inconsistent state.

The Helm chart sets terminationGracePeriodSeconds: 60 (double the shutdown timeout) so Kubernetes sends SIGKILL only after the controller has had a full 30 seconds to drain.

To adjust the timeout:

# values.yaml
terminationGracePeriodSeconds: 120  # increase if reconcile loops routinely take >30s

This upgrades both the kardinal-promoter controller and the bundled krocodile controller to the versions pinned in the new chart version.

Note

kardinal-promoter is backwards-compatible across patch versions. Minor version upgrades may introduce new CRD fields — apply updated CRDs with kubectl apply -f config/crd/bases/ before upgrading the controller.


Uninstall

helm uninstall kardinal-promoter -n kardinal-system

# Optional: remove kardinal CRDs (deletes all Pipelines, Bundles, PolicyGates, etc.)
kubectl delete crd \
  pipelines.kardinal.io \
  bundles.kardinal.io \
  promotionsteps.kardinal.io \
  policygates.kardinal.io \
  prstatuses.kardinal.io \
  rollbackpolicies.kardinal.io

# Optional: remove krocodile CRDs (only if you don't use krocodile elsewhere)
kubectl delete crd \
  graphs.experimental.kro.run \
  graphrevisions.experimental.kro.run

# Optional: remove krocodile namespace
kubectl delete namespace kro-system

RBAC requirements

The kardinal-promoter controller's ServiceAccount requires:

Resource Verbs
pipelines, bundles, promotionsteps, policygates, prstatuses, rollbackpolicies get, list, watch, create, update, patch, delete
graphs.experimental.kro.run get, list, watch, create, update, patch, delete
deployments, services, pods get, list, watch
secrets get (GitHub token secret only)
events create, patch
configmaps get, create, update (leader election + version ConfigMap)

The krocodile controller's ServiceAccount requires full cluster-level access to manage arbitrary resources as directed by Graph specs. This is inherent to its design as a general-purpose DAG engine — it applies resources of any type that appear in Graph node templates.

Both ClusterRole and ClusterRoleBinding resources are created automatically by the Helm chart.


Next steps

  • Quickstart — apply your first Pipeline and promote a Bundle
  • Concepts — understand Pipelines, Bundles, PolicyGates
  • Policy Gates — write CEL expressions for promotion policies
  • Troubleshooting — diagnose installation issues with kardinal doctor