Console administration
Run heartbeat jobs on Kubernetes
Install and operate WarnFire-monitored CronJobs on EKS, AKS, and GKE.
Start with a heartbeat monitor
.
WarnFire publishes docker.io/craigs/warnfire-cli:<release-sha> as a separate
Linux x86-64 container in each server release. The managed CronJob selects
kubernetes.io/arch=amd64; the cluster needs a schedulable x86-64 node. The CLI uses that image as an
init container: it copies the client into a shared volume, then the CronJob
starts the client around your job executable. Your workload image needs
the job, but does not need to bundle WarnFire. Use the client image from the
same release as the CLI on your workstation. Pin both images by digest in
production when your registry supplies the digest.
The commands below create or update resources in a cluster. Run them only
after confirming the --context and namespace. warnfire job install never
chooses your current context implicitly.
Before you install
In Configuration → Heartbeats → Create, make one monitor for this CronJob, choose its service, and save its ping URL once in a private local file. The service’s escalation policy handles missed or failed runs.
Install
warnfireandkubectlon the administrator workstation. Runwarnfire version,warnfire job --help, andkubectl config get-contexts.Choose an existing namespace and an account allowed to get, create, update, and delete Secrets and CronJobs there. The account also needs to read CronJobs for
job status.job installdoes not create namespaces or Kubernetes role bindings.Publish your job image to a registry the cluster can pull. For example, a simple image can copy an executable script to
/app/my-job:FROM alpine:3.22 COPY my-job /app/my-job RUN chmod 0755 /app/my-jobBuild and push this image in your normal CI process. Replace the example script with your real application and dependencies. The job executable must exist at an absolute path inside the image.
Make the CLI image and job image pullable by the nodes. If either image is private, create an image pull Secret in the same namespace and pass its name with
--pull-secret. See Kubernetes private registry guidance .
The CLI image is built, scanned, and published by the WarnFire Forgejo release pipeline. Check its release SHA and digest in the signed accepted release manifest; do not guess a tag that has not been released.
Connect your cluster
Use the provider command that matches your cluster, then record the resulting context name. These commands update your local kubeconfig; they do not install the job. They require your organization’s existing cloud login and cluster access.
Amazon EKS
aws eks update-kubeconfig --region us-west-2 --name MY_CLUSTER --alias warnfire-eks
kubectl --context warnfire-eks get namespace billing
Use the intended AWS identity and region; aws eks update-kubeconfig can also
accept a role ARN. Cluster authentication and Kubernetes authorization are
separate. See the EKS kubeconfig guide
.
Azure AKS
az aks get-credentials --resource-group MY_RESOURCE_GROUP --name MY_CLUSTER
kubectl config get-contexts
kubectl --context MY_CLUSTER get namespace billing
Use the context name printed by kubectl config get-contexts if it differs
from the cluster name. See the
AKS credential command
.
Google Kubernetes Engine (GKE)
gcloud container clusters get-credentials MY_CLUSTER --location us-central1 --project MY_PROJECT
kubectl config get-contexts
kubectl --context GKE_CONTEXT get namespace billing
GKE’s kubectl connection uses gke-gcloud-auth-plugin; install it if your
workstation does not have it. Copy the exact generated context name from
kubectl config get-contexts. See the
GKE kubectl access guide
.
Check the target and install
Set your own values; the following shell variables are examples. Keep the monitor URL out of shell arguments and history.
CTX=warnfire-eks
NS=billing
kubectl --context "$CTX" get namespace "$NS"
kubectl --context "$CTX" -n "$NS" auth can-i get secrets
kubectl --context "$CTX" -n "$NS" auth can-i create secrets
kubectl --context "$CTX" -n "$NS" auth can-i update cronjobs.batch
kubectl --context "$CTX" -n "$NS" auth can-i delete cronjobs.batch
The administrator account needs the same verbs for both secrets and
cronjobs.batch; a no result means cluster access must be granted before
installation. Save the monitor URL in a regular file readable only by your
account (chmod 0600 /path/to/private-ping-url). Then install:
warnfire job install --name nightly-billing --scheduler kubernetes \
--context "$CTX" --namespace "$NS" \
--client-image docker.io/craigs/warnfire-cli:<release-sha> \
--image registry.example.com/billing-jobs:<job-version> \
--schedule '0 2 * * *' --timezone Etc/UTC \
--url-file /path/to/private-ping-url -- /app/my-job
warnfire job status nightly-billing
Add --pull-secret NAME if the nodes need credentials for either image. The
same --name updates the schedule, images, URL, or command later; it does not
move a job to a different context or namespace. To move it, remove it first
from the recorded target, then install in the new target. All install options
are listed in the CLI reference
.
The CLI creates a namespaced Secret named
warnfire-nightly-billing-heartbeat and CronJob named
warnfire-nightly-billing. It refuses to overwrite resources without its
ownership label. It sends the URL to kubectl on standard input. At runtime,
Kubernetes injects the URL from the Secret into the client environment;
the client removes it from the job child process environment. The CronJob
uses Forbid concurrency, no automatic retry, UTC unless you set another
timezone, and no service-account token mount. Kubernetes CronJob timezone
behavior is described in the Kubernetes CronJob documentation
.
Verify a run and diagnose failures
warnfire job status nightly-billing verifies that both managed resources
exist and the CronJob is not suspended. It does not prove that a run finished.
Inspect the schedule and the most recent Job and Pod:
kubectl --context "$CTX" -n "$NS" get cronjob warnfire-nightly-billing
kubectl --context "$CTX" -n "$NS" get jobs
kubectl --context "$CTX" -n "$NS" get pods
kubectl --context "$CTX" -n "$NS" logs JOB_POD_NAME -c job
Check Configuration → Heartbeats → History for the start and terminal
check-ins. The client streams stdout and stderr to the Pod log and sends only
their combined last 16 KiB as a completion diagnostic. Writing to stderr alone
does not determine the outcome. WarnFire applies the monitor’s
exit-code set
;
without a set, 0 succeeds and nonzero codes fail. The wrapper still returns
the original code, so Kubernetes can mark the Job failed even when WarnFire
classifies that nonzero code as successful. Review the workload’s Kubernetes
success criteria separately. Avoid printing secrets in the job output.
To test without waiting for the next schedule, create a one-off Job from the installed CronJob. This runs the workload immediately, so use a harmless test command or a safe environment if the job has side effects:
kubectl --context "$CTX" -n "$NS" create job \
--from=cronjob/warnfire-nightly-billing warnfire-nightly-billing-manual-1
kubectl --context "$CTX" -n "$NS" get job warnfire-nightly-billing-manual-1
For ImagePullBackOff, check both image names and the namespace’s pull
credentials. For a failed init container, inspect
kubectl --context "$CTX" -n "$NS" logs JOB_POD_NAME -c install-warnfire-client;
the client image must match the cluster node architecture. For no check-ins,
verify outbound HTTPS from the job Pod to your WarnFire environment, that the
monitor URL has not been rotated, and that the job ran at the expected
timezone. For a missed deadline, compare the CronJob’s scheduled time, Pod
start time, and the monitor’s grace period.
warnfire job remove nightly-billing deletes the managed CronJob and Secret
from the recorded context and namespace, then removes the local private URL
file. It does not delete the heartbeat monitor or its history. Rotate or
revoke the monitor key if its URL was exposed.