Kubernetes backend
Layout
Per (app, job, schedule-index), cronix manages two resources:
- a
ConfigMapnamedcronix-<app>-<job>-<idx>-speccontaining the per-job spec JSON the trigger shim reads at fire time. - a
CronJobnamedcronix-<app>-<job>-<idx>that mounts the ConfigMap and invokescronix trigger <app>.<job>.
Both resources carry the labels:
cronix.dev/managed: "true"cronix.dev/app: <app>cronix.dev/job: <job>cronix.dev/index: "<idx>"cronix.dev/hash: <hash>cronix uses these labels to enumerate owned resources. Resources without cronix.dev/managed=true are never modified or deleted.
Reconciling a manifest
cronix apply \ --manifest ./billing.cronix.json \ --backend kubernetes \ --k8s-namespace billing \ --k8s-image awbx/cronix:v0.7.2Out-of-cluster runs use --kubeconfig <path> (or KUBECONFIG env / ~/.kube/config). When running cronix itself inside a pod, pass --in-cluster.
cronix list, cronix plan, cronix drift, cronix prune, and cronix show all accept the same backend flags.
Rendering by hand
Operators preferring GitOps-style YAML can still render the resources without instantiating a Backend:
import "github.com/awbx/cronix/go/internal/backends/kubernetes"
yaml, err := kubernetes.RenderManifest( "awbx/cronix:v0.7.2", "billing", // namespace "billing", // app job, // a manifest.NormalizedJob 0, // schedule index "abc123def4567890", // hash specJSON, // the trigger spec, embedded as ConfigMap data)echo "$yaml" | kubectl apply -f -Image
awbx/cronix:<version> is FROM gcr.io/distroless/static, ~20 MB, multi-arch (amd64/arm64). Built and pushed by goreleaser to both DockerHub and GHCR on every v* tag.
Concurrency
The CronJob spec sets concurrencyPolicy: Forbid and backoffLimit: 0 — defense in depth. The shim is still the authoritative concurrency enforcer; K8s preventing duplicate Pods catches misconfigurations early.
Run history
kubectl -n billing get jobs -l cronix.dev/job=reconcile --sort-by=.status.startTimekubectl -n billing logs -l cronix.dev/job=reconcile --max-log-requests 50K8s Events on the CronJob carry skip records when concurrencyPolicy: Forbid fires.
Limitations
- K8s does not support
@every <duration>natively; cronix translates the supported shortcuts (@hourly,@daily, etc.) but rejects@everyat validate time. Use a 5-field cron expression instead. - K8s name length:
cronix-<app>-<job>-<idx>must be ≤ 63 characters total. cronix Validates the (app, job) name combination at apply time. Updateis implemented as Delete+Create rather than Server-Side Apply, so there’s a sub-second gap where a given (CronJob, ConfigMap) pair does not exist. Acceptable in CI deploy contexts.