Monitor Kubernetes CronJobs with Crontiq
Kubernetes CronJobs create pods on a schedule, but kubectl does not tell you if a job produced the right results. A pod can exit with code 0 while processing zero rows. The startingDeadlineSeconds and concurrencyPolicy settings add more failure modes that are easy to miss. You need external monitoring.
Crontiq watches your CronJobs from the outside. If a job does not ping within its expected window, you get an alert. If it sends JSON metrics, Crontiq detects anomalies — like a data export that suddenly processes 10 rows instead of 10,000.
Basic: Curl in the Container Command
The simplest approach: append a curl call to your container command. The ping only fires if the preceding command succeeds.
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-cleanup
namespace: production
spec:
schedule: "0 4 * * *"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 300
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: cleanup
image: myapp/db-cleanup:latest
env:
- name: CRONTIQ_API_KEY
valueFrom:
secretKeyRef:
name: crontiq-secret
key: api-key
command:
- /bin/sh
- -c
- |
python /app/cleanup.py && \
curl -sf https://ping.crontiq.io/p/${CRONTIQ_API_KEY}/db-cleanup
Store your API key in a Kubernetes Secret. The monitor db-cleanup is created automatically on the first successful ping.
Advanced: Start, Success, and Failure Reporting
For jobs that run longer than a few seconds, use the /start endpoint so Crontiq can measure duration. Use a trap to report failures on non-zero exit.
command:
- /bin/sh
- -c
- |
PING_URL="https://ping.crontiq.io/p/${CRONTIQ_API_KEY}/data-export"
# Signal start
curl -sf "${PING_URL}/start" || true
# Run the job
if python /app/export.py --output /tmp/result.json; then
# Report success with metrics from the job output
curl -sf -X POST \
-H "Content-Type: application/json" \
-d @/tmp/result.json \
"${PING_URL}"
else
# Report failure
curl -sf "${PING_URL}/fail" || true
exit 1
fi
Sidecar Pattern for Complex Jobs
If your main container cannot be modified, use a sidecar container that watches the main process and reports to Crontiq. This works well for third-party images.
spec:
template:
spec:
shareProcessNamespace: true
restartPolicy: Never
containers:
- name: main-job
image: vendor/etl-tool:3.2
volumeMounts:
- name: shared
mountPath: /output
- name: crontiq-sidecar
image: curlimages/curl:8.5.0
env:
- name: CRONTIQ_API_KEY
valueFrom:
secretKeyRef:
name: crontiq-secret
key: api-key
command:
- /bin/sh
- -c
- |
# Wait for main container to finish
while [ ! -f /output/done.marker ]; do
sleep 5
done
# Report metrics if available
if [ -f /output/metrics.json ]; then
curl -sf -X POST \
-H "Content-Type: application/json" \
-d @/output/metrics.json \
"https://ping.crontiq.io/p/${CRONTIQ_API_KEY}/vendor-etl"
else
curl -sf "https://ping.crontiq.io/p/${CRONTIQ_API_KEY}/vendor-etl/fail"
fi
volumeMounts:
- name: shared
mountPath: /output
volumes:
- name: shared
emptyDir: {}
Reporting JSON Metrics
Crontiq flattens any JSON and extracts numeric values as trackable metrics. Your job can output arbitrary data without any schema setup.
# Your job writes this JSON:
{"records_exported": 84210, "duration_sec": 45, "destination": {"size_mb": 312.5}}
# Crontiq tracks:
# records_exported = 84210
# duration_sec = 45
# destination.size_mb = 312.5
#
# Moving average + 2 sigma anomaly detection runs automatically.
What Kubernetes Alone Misses
- A pod exits 0 but processed zero records — Kubernetes considers it a success.
- The CronJob is suspended and nobody notices for weeks.
startingDeadlineSeconds causes skipped runs with no alert.
- Resource pressure delays the pod beyond its useful window.
- Crontiq catches all of these by simply asking: did it run, and was it normal?