Deployment Verification¶
Verifying that a new version is healthy right after it ships is one of the most common uses of the operator. There are two ways to do it, and they compose:
- Automatically, on every rollout — declare one TriggeredHealthCheck
and Holmes investigates every future rollout of the service, no matter how it was
triggered (CI, GitOps/Argo sync,
kubectl set image, or a rollback). This is the recommended default — declare once, no per-deploy wiring. - Inline, to gate a pipeline — include a one-time HealthCheck in the deploy manifest (or CI/CD step) and block the pipeline on its result. Use this when CI must wait synchronously for the verdict before proceeding.
A typical setup uses both: a TriggeredHealthCheck for hands-off coverage of all
rollouts, plus an inline HealthCheck in the specific pipeline stage where you want a
hard gate.
Automatic verification with TriggeredHealthCheck¶
Apply this once. From then on, any rollout of a Deployment matching the selector automatically spawns a check — including deploys you didn't make through CI.
# verify-checkout-deploys.yaml — apply once, verifies every future rollout
apiVersion: holmesgpt.dev/v1alpha1
kind: TriggeredHealthCheck
metadata:
name: verify-checkout-deploys
namespace: production
spec:
deploymentRollout:
selector:
matchLabels:
app: checkout-api
delaySeconds: 300 # wait 5m after the rollout, then check (default)
query: |
checkout-api was just rolled out to {{ .new.image }} (previously {{ .old.image }}).
Is the new version healthy? Compare error rates, latency, restarts, and logs
before vs after the rollout and flag any regressions.
timeout: 120
mode: alert
destinations:
- type: slack
config:
channel: "#deploy-alerts"
kubectl apply -f verify-checkout-deploys.yaml
# After a deploy, see the check it produced and the verdict
kubectl get hc -n production -l holmesgpt.dev/triggered-by=verify-checkout-deploys
kubectl describe thc verify-checkout-deploys -n production
The {{ .new.image }} / {{ .old.image }} tokens are substituted with the rollout's
before/after images, so the investigation knows exactly what changed. See
Triggered Health Checks for the full field reference and
how long the check waits after a rollout.
Catch slow-burn regressions too
Some problems (memory leaks, connection-pool exhaustion) only appear after the new
version has run for a while. Add a second trigger with a delay — e.g.
delaySeconds: 86400 — to re-investigate the same rollout a day later, or use a
ScheduledHealthCheck for continuous coverage.
Gating CI/CD with an inline HealthCheck¶
When a pipeline must wait for the verdict before promoting a release, include a
one-time HealthCheck in the same manifest as your deployment. It runs immediately after
kubectl apply and reports whether the new version started correctly.
# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: checkout-api
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: checkout-api
template:
metadata:
labels:
app: checkout-api
spec:
containers:
- name: checkout-api
image: myregistry/checkout-api:v2.4.1
---
apiVersion: holmesgpt.dev/v1alpha1
kind: HealthCheck
metadata:
name: checkout-api-deploy-v2-4-1
namespace: production
labels:
app: checkout-api
deploy-version: v2.4.1
spec:
query: "We just rolled out a new version of checkout-api to production. Is the deployment healthy? Check logs, error rates, latency, and resource usage before vs after the deploy and flag any regressions."
timeout: 120
mode: alert
destinations:
- type: slack
config:
channel: "#deploy-alerts"
Then poll for the result to gate the pipeline:
# Wait for the check to complete, then read the result
for i in $(seq 1 30); do
RESULT=$(kubectl get hc checkout-api-deploy-v2-4-1 -n production -o jsonpath='{.status.result}' 2>/dev/null)
if [ "$RESULT" = "pass" ]; then
echo "Deploy verified healthy"
exit 0
elif [ "$RESULT" = "fail" ] || [ "$RESULT" = "error" ]; then
echo "Deploy check failed:"
kubectl get hc checkout-api-deploy-v2-4-1 -n production -o jsonpath='{.status.message}'
exit 1
fi
sleep 10
done
echo "Timed out waiting for health check"
exit 1
If pods crash or fail readiness, the check fails and the pipeline stops.
When to use which¶
| TriggeredHealthCheck | Inline HealthCheck | |
|---|---|---|
| Runs on | Every rollout, automatically | Only when you apply it |
| Setup | Declare once per service | Added to each deploy manifest/step |
Covers out-of-band deploys (kubectl set image, GitOps, rollback) |
Yes | No |
| Blocks a CI/CD pipeline | No (fire-and-forget) | Yes (poll the result to gate) |
Tips¶
- Version the inline check name (e.g.,
checkout-api-deploy-v2-4-1) so each deploy creates a distinct resource and you keep an audit trail. This applies to one-timeHealthCheckresources only —TriggeredHealthCheckandScheduledHealthCheckuse a fixed name and create child HealthChecks automatically. - Set a longer timeout (60–120s) to give the investigation time to gather data.
- Use labels like
deploy-versionto query checks for a specific release:kubectl get hc -l deploy-version=v2.4.1. - Combine with ArgoCD: the query can reference sync status — e.g., "Is the ArgoCD application 'checkout-api' synced and healthy with no degraded resources?" — since Holmes has access to the ArgoCD toolset.