Skip to content

GitOps Integration Guide

This directory contains configuration for integrating the Neo4j Kubernetes Operator with GitOps tools (ArgoCD, Flux) and Prometheus monitoring.

ArgoCD Health Checks

ArgoCD does not natively understand status.phase on custom resources and shows everything as "Progressing". Apply the health check ConfigMap to teach ArgoCD how to interpret Neo4j operator resource states.

kubectl patch configmap argocd-cm -n argocd \
  --type merge --patch-file docs/gitops/argocd-health-checks.yaml

Health state mapping:

ArgoCD Status Neo4j Phase(s)
Healthy Ready, Completed, Succeeded, Installed; Neo4jBackup also Scheduled and Suspended
Degraded Failed, Degraded; Neo4jBackup and Neo4jPlugin also Invalid
Progressing Forming, Pending, Creating, Waiting, Running, or empty

A Neo4jBackup with spec.schedule stays in Scheduled for its whole life (it never reaches Completed), and spec.suspend: true is declared state rather than a fault, so both map to Healthy — otherwise an Application containing a scheduled backup would show Progressing forever and hold up any later sync wave. The Ready condition (what Flux reads) agrees for Scheduled: it is True with reason BackupScheduled. Suspended still reports Ready=False (a suspended backup is not ready to run) but with reason Suspended, not ReconciliationFailed: nothing failed. Invalid (a spec the validator rejected) is terminal until the spec is edited, so it maps to Degraded rather than Progressing.

A Neo4jEnterpriseCluster that loses a server stays Ready — Healthy — for a grace period (default 5 minutes), so a routine pod restart does not flip the Application. If the server is still missing after that, the phase turns Degraded and so does the Application; it returns to Healthy when the server does. A cluster that loses a majority of its servers reports Forming, which ArgoCD shows as Progressing. See Server availability.

Health checks are configured for all 27 CRDs in the neo4j.neo4j.com group — the 15 self-managed CRDs (7 workload, 4 identity, 4 composite / alias / replication) and all 12 Aura CRDs.

Self-managed CRDs key off status.phase, per the table above.

Aura CRDs key off the Ready condition instead. Their status.phase largely mirrors Aura's own API status (AuraInstance copies the live instance status verbatim), which is an open vocabulary Neo4j can extend without a version bump — enumerating running states would silently report any new one as Degraded. phase is still consulted first for the terminal Error/Failed values, because the Ready condition can lag a failure by a reconcile.

A promoted replica reports Healthy, not Degraded

Neo4jReplicaDatabase reaching phase: Promoted is a completed failover, not a fault — its health check maps that to Healthy. Mapping it to Degraded would show a successful DR promotion as broken, and could prompt an operator (or an automated remediation) to try to "fix" something that is working as designed. The CR is inert from that point on and will never modify the database again.

Flux Health Checks

Flux automatically detects readiness via status.conditions when CRDs expose a standard Ready condition (type Ready, using metav1.Condition). No extra Flux configuration is needed once the operator surfaces that condition.

A Neo4jEnterpriseCluster keeps Ready=True through a short server outage (the grace period above); a Degraded one reports Ready=False with reason ClusterDegraded, so a Kustomization health-checking it fails until the missing server is back.

Prometheus ServiceMonitor

The Helm chart includes a ServiceMonitor for the Prometheus Operator. By default (metrics.secure: true) the operator serves /metrics over HTTPS with bearer-token authentication (TokenReview + SubjectAccessReview against the metrics-reader ClusterRole), so the ServiceMonitor needs a token Secret for a ServiceAccount bound to that ClusterRole — the chart refuses to render otherwise. Enable it at install or upgrade time:

helm upgrade --install neo4j-operator charts/neo4j-operator \
  --set metrics.enabled=true \
  --set metrics.serviceMonitor.enabled=true \
  --set metrics.serviceMonitor.bearerTokenSecret.name=<token-secret>

Or set in values.yaml:

metrics:
  enabled: true
  serviceMonitor:
    enabled: true
    interval: "30s"
    scrapeTimeout: "10s"
    labels: {}        # add Prometheus instance selector labels here if needed
    bearerTokenSecret:
      name: <token-secret>   # kubernetes.io/service-account-token Secret, key "token"

For a legacy scraper that cannot present a bearer token, set metrics.secure: false instead (plain HTTP, no authn/authz — only behind a NetworkPolicy you trust).

The metrics Service listens on port 8080 at /metrics (Prometheus text format).