A simple Kubernetes Operator (built with Kubebuilder) that reconciles a
CronJob custom resource (batch.martishin.com/v1, Kind=CronJob).
For each CronJob, the controller ensures Kubernetes Jobs are created
and executed on schedule, similar to the built-in CronJob controller.
The project is built with Bazel and packaged/deployed with Timoni.
Kubebuilder’s kustomize manifests remain for development/CI, but Timoni is
authoritative for installs and upgrades.
- Description
- Prerequisites
- Build & Release (Bazel + Timoni)
- Local development
- Project structure
- Contributing
- License
Goal of the controller
The controller watches CronJob resources and reconciles the desired state:
- Uses
.spec.schedule(cron string) and.spec.jobTemplateto create Jobs. - Maintains
.statusfields (successful/failed job history). - Ensures Jobs run at the expected times.
This demonstrates a production-like operator pattern.
- Go
- Docker (for building images)
- kubectl (any recent version)
- Timoni CLI (install from https://timoni.sh/)
- Bazel (for builds)
- Access to a Kubernetes cluster (Kind is easiest).
Optional:
- Kind CLI (for local cluster)
- make
The Timoni bundle lives at:
timoni/bundles/operator-stack.cue
It includes three instances:
- crds – the CronJob CRD
- operator – controller (RBAC, SA, Deployment, metrics)
- sample – a sample CronJob resource
# 1) (Optional) Create a Kind cluster
kind create cluster --name mycluster
# 2) Build controller image with Bazel
bazel build //:controller_image
# 3) Load the image into Kind
bazel run //:controller_image_load
docker tag kubebuilder/cronjob-controller:dev controller:dev
kind load docker-image controller:dev --name mycluster
# 4) Point Timoni bundle to that image
make bundle-set-image IMG=controller:dev
# 5) Apply with Timoni
make bundle-apply# Preview manifests
make bundle-build | less
# Diff vs live cluster
make bundle-diff
# Apply & wait
make bundle-apply
# Status
make bundle-status
kubectl -n cronjob-operator-system get deploy,pods# Inspect the sample CronJob
kubectl -n default get cronjob.batch.martishin.com cronjob-sample -o yaml
# Watch Jobs being created
kubectl -n default get jobs -w
# Inspect sample pod logs
kubectl -n default get podsmake bundle-deletemake runmake test
make test-e2ebazel-kubebuilder-timoni-k8s-cronjob-operator/
├── api/v1/ # CRD Go types
├── cmd/main.go # Controller entrypoint
├── internal/controller/ # Reconciler logic
├── timoni/
│ ├── bundles/operator-stack.cue
│ └── modules/
│ ├── cronjob-operator-crds/ # CRD instance
│ ├── cronjob-operator/ # Operator instance
│ └── cronjob-sample/ # Sample CronJob
├── BUILD.bazel, MODULE.bazel # Bazel build files
└── config/ # Kubebuilder kustomize (dev only)
PRs welcome!
make lint
make testCopyright 2025.
Licensed under the Apache License, Version 2.0. See LICENSE for details.