Argo CD
Argo CD does not interpret arbitrary custom-resource status. Unknown CRDs are
reported Healthy as soon as they exist, so a DiscoveryPolicy that is failing
to reach its registry, or a CachedImage still pulling, would show green.
drop already exposes the idiomatic Kubernetes status shape — status.phase and
status.conditions[] with a Ready condition — but Argo CD needs a small Lua
health check to translate it. Install the checks below to get real health in the
Argo CD UI, accurate Application health, and the ability to gate sync waves on
CachedImage/CachedImageSet readiness.
Status drop exposes
| Kind | status.phase | Ready condition | Notes |
|---|---|---|---|
CachedImage | Pending | Pulling | Ready | Degraded | yes (reason/message) | status.ready is a nodesReady/nodesTargeted string |
CachedImageSet | Pending | Ready | Degraded | yes | aggregates child CachedImages |
DiscoveryPolicy | — | yes (Synced / error reason) | no phase; health comes from the Ready condition |
PullPolicy | — | — | config-only, no status → always Healthy |
Health checks
Add the following to the argocd-cm ConfigMap in the Argo CD install namespace.
The keys use the standard resource.customizations.health.<group>_<kind> form.
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
labels:
app.kubernetes.io/name: argocd-cm
app.kubernetes.io/part-of: argocd
data:
# CachedImage: gate on phase, fall back to the Ready condition.
resource.customizations.health.drop.corewire.io_CachedImage: |
hs = {}
if obj.status == nil then
return { status = "Progressing", message = "Waiting for status" }
end
-- Wait for the controller to observe the latest spec.
if obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil
and obj.status.observedGeneration < obj.metadata.generation then
return { status = "Progressing", message = "Reconciling" }
end
local msg = obj.status.ready or ""
if obj.status.conditions ~= nil then
for _, c in ipairs(obj.status.conditions) do
if c.type == "Ready" and c.message ~= nil and c.message ~= "" then
msg = c.message
end
end
end
if obj.status.phase == "Ready" then
return { status = "Healthy", message = msg }
end
if obj.status.phase == "Degraded" then
return { status = "Degraded", message = msg }
end
-- Pending / Pulling / empty
return { status = "Progressing", message = msg }
# CachedImageSet: same shape, phase is Pending | Ready | Degraded.
resource.customizations.health.drop.corewire.io_CachedImageSet: |
if obj.status == nil then
return { status = "Progressing", message = "Waiting for status" }
end
if obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil
and obj.status.observedGeneration < obj.metadata.generation then
return { status = "Progressing", message = "Reconciling" }
end
local managed = obj.status.imagesManaged or 0
local ready = obj.status.imagesReady or 0
local msg = tostring(ready) .. "/" .. tostring(managed) .. " images ready"
if obj.status.conditions ~= nil then
for _, c in ipairs(obj.status.conditions) do
if c.type == "Ready" and c.message ~= nil and c.message ~= "" then
msg = c.message
end
end
end
if obj.status.phase == "Ready" then
return { status = "Healthy", message = msg }
end
if obj.status.phase == "Degraded" then
return { status = "Degraded", message = msg }
end
return { status = "Progressing", message = msg }
# DiscoveryPolicy: no phase — health is the Ready condition.
resource.customizations.health.drop.corewire.io_DiscoveryPolicy: |
if obj.status == nil or obj.status.conditions == nil then
return { status = "Progressing", message = "Waiting for first sync" }
end
if obj.metadata.generation ~= nil and obj.status.observedGeneration ~= nil
and obj.status.observedGeneration < obj.metadata.generation then
return { status = "Progressing", message = "Reconciling" }
end
for _, c in ipairs(obj.status.conditions) do
if c.type == "Ready" then
if c.status == "True" then
return { status = "Healthy", message = c.message }
end
return { status = "Degraded", message = c.message }
end
end
return { status = "Progressing", message = "No Ready condition yet" }
# PullPolicy: configuration-only, no status.
resource.customizations.health.drop.corewire.io_PullPolicy: |
return { status = "Healthy", message = "Configuration resource" }If your Argo CD version prefers the list-based form, the same logic can be placed
under the resourceHealthChecks key instead — one entry per group/kind.
Applying it
- Argo CD Helm chart / Kustomize install: set these under
configs.cm(resource.customizations.health.*) in your Argo CD values, or patch theargocd-cmConfigMap. Health checks are a cluster-wide Argo CD setting — they are read fromargocd-cmand apply to everyApplicationandAppProject; Argo CD has no per-Applicationor per-AppProjecthealth-check override. - Reload:
argocd-cmis picked up automatically; no restart is required.
Sync waves
Because the checks report Progressing until a resource is actually Ready,
you can make a later sync wave wait for caching to finish. For example, keep the
drop operator on an earlier wave and place CachedImageSet resources on a later
wave; Argo CD will hold the wave until the sets report Healthy.