Declarative workflow automation for Kubernetes. Define event-driven automations as CRDs — no web UI, no proprietary runtime, no vendor lock-in.
Trigger on webhooks, Kafka messages, cron schedules, or Kubernetes resource events. Execute multi-step flows with HTTP calls, data transforms, conditional branching, timed waits, and retry policies — all declared in YAML and version-controlled alongside your infrastructure.
Credentials never leave the cluster's trust boundary. The controller resolves secrets in-memory and hands a fully-substituted request to a dedicated, low-privilege HTTP executor pod — no Kubernetes API access, no secrets RBAC, independent SSRF protection. Your API keys are never written to etcd and never stored in a separate workflow database.
One model, not a system of systems. A workflow is Trigger → Flow → FlowRun — three CRDs, one mental model, from event to result. Every execution is a real Kubernetes object you can kubectl get end to end: what fired it, what each step did, and why.
kubectl configured against your clusterkustomize v5+ (or kubectl 1.27+ which bundles it)# Install CRDs
kubectl apply -k config/crd
# Deploy the operator
kubectl apply -k config/default
The controller starts in kubezap-system. Verify it is running:
kubectl get pods -n kubezap-system
helm install kubezap oci://ghcr.io/kubezap/charts/kubezap-operator \
--namespace kubezap-system --create-namespace
See docs/overview.md for install-mode options (OwnNamespace, SingleNamespace, MultiNamespace) and image-override examples.
KubeZap is listed on the community OperatorHub (k8s-operatorhub/community-operators, for vanilla Kubernetes). Install via the OperatorHub.io "Install" button, which generates the Subscription for your target namespace.
See docs/contributing.md for build instructions, local k3s setup, and how to run unit and e2e tests.
Once the operator is running, define a Trigger and a Flow:
# trigger.yaml
apiVersion: automation.kubezap.io/v1alpha1
kind: Trigger
metadata:
name: my-webhook
namespace: default
spec:
type: webhook
enabled: true
webhook:
path: /hooks/my-webhook
method: POST
flowRef:
name: my-flow
---
# flow.yaml
apiVersion: automation.kubezap.io/v1alpha1
kind: Flow
metadata:
name: my-flow
namespace: default
spec:
steps:
- name: call-api
action:
type: http
http:
url: https://httpbin.org/post
method: POST
body: '{"event": "$(trigger.body.event)"}'
kubectl apply -f trigger.yaml -f flow.yaml
# Send a test event
curl -X POST http://<gateway-ip>:8080/hooks/my-webhook \
-H 'Content-Type: application/json' \
-d '{"event": "hello"}'
# Inspect the execution
kubectl get flowruns -n default
kubectl get flowrun <name> -o jsonpath='{.status.phase}'
For a full walkthrough see examples/order-router/.
| Document | Description |
|---|---|
| Overview | Architecture, core concepts, CRD reference |
| Getting Started | Step-by-step guide: webhook → transform → conditional notify |
| Examples | Runnable examples with manifests and step-by-step instructions |
| Flow CRD | Full step action reference (http, transform, publish, wait) |
| FlowRun CRD | Execution model, GC, status fields |
| Integration CRD | Kafka, plugin protocol |
| Webhook Security | HMAC, bearer, OIDC, API-key, IP allowlist, mTLS |
| Observability | Prometheus metrics, OpenTelemetry traces, access logs |
kubezap.io/retain annotation, max FlowRun cap per Triggerkubezap command — watch, history, triggers, flows subcommandsWATCH_NAMESPACES supports MultiNamespace, SingleNamespace, OwnNamespaceHave a question, an idea, or want to show off what you built? Use GitHub Discussions — it's the place for Q&A, feature ideas, and community show-and-tell. Found a bug? Open an issue instead.
Apache 2.0
Go
94.7%
Shell
3.4%
Makefile
1.4%
Declarative workflow automation for Kubernetes. Define event-driven automations as CRDs — no web UI, no proprietary runtime, no vendor lock-in.
Trigger on webhooks, Kafka messages, cron schedules, or Kubernetes resource events. Execute multi-step flows with HTTP calls, data transforms, conditional branching, timed waits, and retry policies — all declared in YAML and version-controlled alongside your infrastructure.
Credentials never leave the cluster's trust boundary. The controller resolves secrets in-memory and hands a fully-substituted request to a dedicated, low-privilege HTTP executor pod — no Kubernetes API access, no secrets RBAC, independent SSRF protection. Your API keys are never written to etcd and never stored in a separate workflow database.
One model, not a system of systems. A workflow is Trigger → Flow → FlowRun — three CRDs, one mental model, from event to result. Every execution is a real Kubernetes object you can kubectl get end to end: what fired it, what each step did, and why.
kubectl configured against your clusterkustomize v5+ (or kubectl 1.27+ which bundles it)# Install CRDs
kubectl apply -k config/crd
# Deploy the operator
kubectl apply -k config/default
The controller starts in kubezap-system. Verify it is running:
kubectl get pods -n kubezap-system
helm install kubezap oci://ghcr.io/kubezap/charts/kubezap-operator \
--namespace kubezap-system --create-namespace
See docs/overview.md for install-mode options (OwnNamespace, SingleNamespace, MultiNamespace) and image-override examples.
KubeZap is listed on the community OperatorHub (k8s-operatorhub/community-operators, for vanilla Kubernetes). Install via the OperatorHub.io "Install" button, which generates the Subscription for your target namespace.
See docs/contributing.md for build instructions, local k3s setup, and how to run unit and e2e tests.
Once the operator is running, define a Trigger and a Flow:
# trigger.yaml
apiVersion: automation.kubezap.io/v1alpha1
kind: Trigger
metadata:
name: my-webhook
namespace: default
spec:
type: webhook
enabled: true
webhook:
path: /hooks/my-webhook
method: POST
flowRef:
name: my-flow
---
# flow.yaml
apiVersion: automation.kubezap.io/v1alpha1
kind: Flow
metadata:
name: my-flow
namespace: default
spec:
steps:
- name: call-api
action:
type: http
http:
url: https://httpbin.org/post
method: POST
body: '{"event": "$(trigger.body.event)"}'
kubectl apply -f trigger.yaml -f flow.yaml
# Send a test event
curl -X POST http://<gateway-ip>:8080/hooks/my-webhook \
-H 'Content-Type: application/json' \
-d '{"event": "hello"}'
# Inspect the execution
kubectl get flowruns -n default
kubectl get flowrun <name> -o jsonpath='{.status.phase}'
For a full walkthrough see examples/order-router/.
| Document | Description |
|---|---|
| Overview | Architecture, core concepts, CRD reference |
| Getting Started | Step-by-step guide: webhook → transform → conditional notify |
| Examples | Runnable examples with manifests and step-by-step instructions |
| Flow CRD | Full step action reference (http, transform, publish, wait) |
| FlowRun CRD | Execution model, GC, status fields |
| Integration CRD | Kafka, plugin protocol |
| Webhook Security | HMAC, bearer, OIDC, API-key, IP allowlist, mTLS |
| Observability | Prometheus metrics, OpenTelemetry traces, access logs |
kubezap.io/retain annotation, max FlowRun cap per Triggerkubezap command — watch, history, triggers, flows subcommandsWATCH_NAMESPACES supports MultiNamespace, SingleNamespace, OwnNamespaceHave a question, an idea, or want to show off what you built? Use GitHub Discussions — it's the place for Q&A, feature ideas, and community show-and-tell. Found a bug? Open an issue instead.
Apache 2.0
Go
94.7%
Shell
3.4%
Makefile
1.4%