WarnFireDocs

Console administration

Run heartbeat jobs on Kubernetes

Install and operate WarnFire-monitored CronJobs on EKS, AKS, and GKE.

Version 1.0 Status Needs verification For Tenant administratorsFor Tenant owners heartbeatskubernetescli

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

  1. 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.

  2. Install warnfire and kubectl on the administrator workstation. Run warnfire version, warnfire job --help, and kubectl config get-contexts.

  3. 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 install does not create namespaces or Kubernetes role bindings.

  4. 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-job
    

    Build 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.

  5. 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.