Skip to content

Neo4jEnterpriseCluster API Reference

The Neo4jEnterpriseCluster Custom Resource Definition (CRD) manages Neo4j Enterprise clusters with high availability, automatic failover, and horizontal scaling capabilities.

Overview

  • API Version: neo4j.neo4j.com/v1beta1
  • Kind: Neo4jEnterpriseCluster
  • Supported Neo4j Versions: 5.26.x (last semver LTS) and 2025.01.0+ (CalVer)
  • Architecture: Server-based deployment with unified StatefulSet
  • Minimum Servers: 2 (required for clustering)
  • Maximum Servers: 100 (validated limit)
  • Name Limit: metadata.name is at most 56 characters (generated resources append suffixes; validated)

Architecture

Server-Based Architecture: Neo4jEnterpriseCluster uses a unified server-based architecture:

  • Single StatefulSet: {cluster-name}-server with configurable replica count
  • Server Pods: Named {cluster-name}-server-0, {cluster-name}-server-1, etc.
  • Self-Organization: Servers automatically organize into primary/secondary roles for databases
  • Backup: Use the Neo4jBackup CRD — each CR spawns a Kubernetes Job. There is no persistent backup pod or sidecar.
  • Role Flexibility: Servers can host multiple databases with different roles

When to Use:

  • Production workloads requiring high availability
  • Multi-database deployments with different topology requirements
  • Workloads needing horizontal read scaling
  • Enterprise features like advanced backup, security, and monitoring
  • Large datasets requiring property sharding (Neo4j 2025.12+)

For single-node deployments, use Neo4jEnterpriseStandalone instead.

Spec

The Neo4jEnterpriseClusterSpec defines the desired state of a Neo4j Enterprise cluster.

Core Configuration

Field Type Required Description
image ImageSpec Required The Neo4j Docker image configuration
topology TopologyConfiguration Required Cluster topology (number of servers)
storage StorageSpec Required Storage configuration for data persistence
acceptLicenseAgreement string (yes | eval) Required Your explicit acceptance of the Neo4j Enterprise license. yes accepts the commercial license agreement; eval accepts the 30-day evaluation license. This operator is independent and does not accept the license on your behalf — you must hold your own valid Neo4j Enterprise license. The operator refuses to deploy until this is set.
auth AuthSpec Optional Authentication configuration

Kubernetes Integration

Field Type Description
resources *corev1.ResourceRequirements CPU and memory resources
env []corev1.EnvVar Environment variables for Neo4j pods
nodeSelector map[string]string Node selector constraints
tolerations []corev1.Toleration Pod tolerations
affinity *corev1.Affinity Pod affinity rules
securityContext *SecurityContextSpec Pod/container security overrides

Neo4j Configuration

Field Type Description
config map[string]string Custom Neo4j configuration

Operations

Field Type Description
upgradeStrategy UpgradeStrategySpec Upgrade strategy configuration

To back up a cluster, create a separate Neo4jBackup CR. To restore a cluster from a backup, create a separate Neo4jRestore CR after the cluster reaches Ready — see Neo4jRestore.

Networking

Field Type Description
service ServiceSpec Service configuration for external access

Extensions

Field Type Description
tls TLSSpec TLS configuration
mcp MCPServerSpec MCP server deployment and exposure settings
propertySharding PropertyShardingSpec Property sharding configuration (Neo4j 2025.12+)
monitoring MonitoringSpec Query monitoring configuration
audit AuditSpec Compliance-oriented logging — security.log + query.log tuning for PCI / HIPAA / GDPR. audit.enabled=true is a one-flag opt-in to secure-by-default query-literal redaction.
networkPolicy NetworkPolicySpec Optional NetworkPolicy emission that restricts ingress to port 6362 to operator-managed backup pods. Disabled by default. Requires a CNI that enforces NetworkPolicy (Calico/Cilium/Antrea/Weave).
crossClusterReplication CrossClusterReplicationSpec Network-mode cross-cluster database replication (CCDR): deploys a self-hosted TCP proxy exposing this cluster's tx-shipping port (6000) externally via one LoadBalancer Service, for use by a Neo4jReplicaDatabase with source.mode: network in another Kubernetes cluster. Disabled by default. Requires tls.mode: cert-manager — the proxy is a TCP passthrough that authenticates nothing, so the cluster SSL policy is the only access control on the exposed port; enabling it without TLS is rejected.
auraFleetManagement AuraFleetManagementSpec Aura Fleet Management integration (optional)
trustedCASecrets []TrustedCASecret CA bundles to add to Neo4j's JVM truststore (OIDC, LDAPS, plugin downloads, peer-cluster replication)
serverDefaultCypherLanguage string Cypher version a database gets when it is created without one of its own — Neo4j's db.query.default_language. CYPHER_5 or CYPHER_25. Unset, the operator picks once and records it in status.effectiveCypherLanguage: CYPHER_25 for a deployment it creates on a CalVer image, CYPHER_5 on the 5.26 LTS, and whatever an existing deployment already runs — an operator upgrade never changes it. Neo4j fixes a database's language at creation, so this applies only to databases created afterwards; change an existing one with ALTER DATABASE … SET DEFAULT LANGUAGE. Changing the field restarts the servers. CYPHER_25 needs a CalVer image, and nothing is ever written on the LTS (the setting does not exist there). Don't also set db.query.default_language in spec.config; the two are refused when they disagree.
remoteAliasKeystore RemoteAliasKeystoreSpec Keystore used to encrypt the credentials of remote database aliases that store native credentials — including the remote constituents of a Neo4jCompositeDatabase. Required for that mode only; aliases using OIDC credential forwarding store no credential and need no keystore.
extraVolumes []corev1.Volume Arbitrary pod volumes mounted into the Neo4j pod; reference them via extraVolumeMounts
extraVolumeMounts []corev1.VolumeMount Mount points for extraVolumes (or, rarely, operator-managed volumes); operator-managed paths are rejected by the validator
extraEnvFrom []corev1.EnvFromSource Standard Kubernetes pass-through. Projects entire Secrets or ConfigMaps as environment variables onto the Neo4j container. Intended for credential bundles (e.g. cloud seed credentials for seedURI-based restores).
podServiceAccountAnnotations map[string]string Annotations stamped onto the operator-managed ServiceAccount the Neo4j server pods run under. Use for cloud Workload Identity so the JVM can assume a cloud role when it fetches a seed URI (cluster restore, Neo4jDatabase/Neo4jShardedDatabase seeding) — e.g. eks.amazonaws.com/role-arn (AWS IRSA), iam.gke.io/gcp-service-account (GKE WI), or the Azure WI labels. Merged additively (operator owns only the keys you list; out-of-band annotations preserved). Set this before the pods start (or roll the StatefulSet after adding it) so the identity webhook injects the token at pod admission.

Type Definitions

ImageSpec

Field Type Description
repo string Required. Image repository (e.g. "neo4j")
tag string Required. Image tag
pullPolicy string Pull policy: "Always", "IfNotPresent", "Never" (default: "IfNotPresent")
pullSecrets []string Image pull secrets

TopologyConfiguration

Defines the cluster server topology and role constraints.

Field Type Description
servers int32 Required. Number of Neo4j servers (minimum: 2, maximum: 100)
minSystemPrimaries *int32 System-database primary count written to dbms.cluster.minimum_initial_system_primaries_count at cluster formation, and the floor the cluster can later be scaled down to. Default: min(3, servers). Validator: must be ≥ 2 and ≤ servers; an even value draws a warning (an even voting set has no clean write-quorum majority — prefer 3, 5, …). It also sets each server's role for the system database: servers with an ordinal below it start as system primaries, the rest as system secondaries (fixed at a server's first start and recorded on its volume; servers created before this behaviour keep their role). Adding servers past it does not restart the running servers. See System primaries and secondaries
serverModeConstraint string Global server mode constraint: "NONE" (default), "PRIMARY", "SECONDARY"
serverRoles []ServerRoleHint Per-server role constraints (overrides global constraint)
placement *PlacementConfig Advanced placement and scheduling configuration
availabilityZones []string Target availability zones for server distribution
enforceDistribution bool Enforce server distribution across topology domains

Server Role Management:

  • Servers self-organize into primary/secondary roles at the database level
  • Role constraints influence which databases a server can host:
  • NONE: Server can host databases in any mode (default)
  • PRIMARY: Server only hosts databases in primary mode
  • SECONDARY: Server only hosts databases in secondary mode
  • Use serverRoles for granular per-server control

Validation:

  • Minimum 2 servers required for clustering
  • Cannot configure all servers as SECONDARY (cluster needs primaries)
  • Server indices in serverRoles must be within range (0 to servers-1)

ServerRoleHint

Specifies role constraints for individual servers.

Field Type Description
serverIndex int32 Required. Server index (0-based, must be < servers count)
modeConstraint string Required. Role constraint: "NONE", "PRIMARY", "SECONDARY"

StorageSpec

Field Type Description
className string Storage class for the data PVCs. Optional — if omitted, the PVCs inherit the cluster's default StorageClass. When set, the named class must exist: the operator reports an explicit error (a StorageClassNotFound event and Failed status) rather than leaving server pods Pending. Immutable after creation.
size string Required. Storage size (e.g., "10Gi"). Must be a valid Kubernetes quantity greater than zero, the same rule as a standalone: "1.5Gi" is fine; "0", "fifty", "10 Gi" and a capital K suffix ("5K", not a Kubernetes suffix — use Ki, or lowercase k) are refused by validation. Can be increased after creation — the operator automatically expands PVCs and recreates the StatefulSet with zero downtime. Cannot be decreased (PVC shrink is not supported by Kubernetes). Requires the StorageClass to have allowVolumeExpansion: true.
retentionPolicy string PVC retention policy: "Delete" (default) permanently removes PVCs on deletion; "Retain" preserves them. Use Retain for production to prevent data loss. See Storage and PVC Retention.

AuthSpec

Field Type Description
authenticationProviders []string Ordered list of authentication providers (e.g., ["ldap", "native"]), written into dbms.security.authentication_providers. Values: native, ldap, oidc-<name>, plugin-<name>; the older oidc, kerberos, jwt, saml, custom validate with a warning. Default: ["native"]
authorizationProviders []string Ordered list of authorization providers, written into dbms.security.authorization_providers. Same values. Default: ["native"]
adminSecret string Secret containing admin username and password (keys: username, password)
externalSecrets *ExternalSecretsConfig External secrets configuration
ldap *Neo4jLDAPSpec LDAP authentication and authorization configuration
oidc map[string]Neo4jOIDCProviderSpec OIDC/SSO providers. Map key = provider name (used in authenticationProviders as oidc-<key>). For JWT-based auth, use OIDC — Neo4j ID tokens are JWTs.
authCacheTTL string Auth cache TTL (e.g., "10m", "600s"). Maps to dbms.security.auth_cache_ttl
trustStore *SecretKeyRef Custom JVM truststore for LDAPS or OIDC with internal CAs

Neo4jLDAPSpec

Field Type Description
host string Required. LDAP server URL (e.g., ldap://host:389 or ldaps://host:636)
useStartTLS *bool Enable STARTTLS (use with ldap://, not ldaps://). Defaults to true when host starts with ldap:// and the field is unset — the Neo4j security checklist's secure-by-default. Set to false explicitly to opt out (dev / mock-LDAP setups).
authentication *LDAPAuthenticationSpec User authentication settings
authorization *LDAPAuthorizationSpec Group/role authorization settings
debugGroupLogging *bool Debug-level LDAP group logging (disable in production)

LDAPAuthenticationSpec

Field Type Description
userDNTemplate string DN template for binding. {0} = username. Example: "{0}@example.com"
searchForAttribute *bool Use attribute-based user lookup instead of DN template
attribute string LDAP attribute to search (e.g., "samaccountname")
cacheEnabled *bool Cache authentication results

LDAPAuthorizationSpec

Field Type Description
userSearchBase string Base DN for user search (e.g., "dc=example,dc=com")
userSearchFilter string LDAP filter for user search. {0} = username
groupMembershipAttributes []string User attributes containing group membership (e.g., ["memberOf"])
groupToRoleMapping map[string]string Map of LDAP group DNs to comma-separated Neo4j roles
accessPermittedGroup string Restrict access to members of this group
useSystemAccount *bool Use a system account for authorization queries
systemAccountSecretRef string Secret name with username and password keys for the system account
nestedGroupsEnabled *bool Enable recursive group resolution
nestedGroupsSearchFilter string Filter for nested group lookups. {0} = user DN

Neo4jOIDCProviderSpec

⚠️ All URI fields require https:// — Neo4j 2026.x rejects http:// for wellKnownDiscoveryURI, authEndpoint, tokenEndpoint, jwksURI, userInfoURI, and issuer at config-parse time, and the cluster fails to start. There is no insecure-mode override. Self-hosted IDPs without TLS need a TLS-terminating proxy plus the proxy's CA in spec.trustedCASecrets.

Field Type Description
displayName string Display name on login screen
wellKnownDiscoveryURI string OIDC discovery endpoint (auto-configures other endpoints). HTTPS only.
authEndpoint string Authorization endpoint (manual override). HTTPS only.
tokenEndpoint string Token endpoint (manual override). HTTPS only.
jwksURI string JWKS endpoint (manual override). HTTPS only.
userInfoURI string UserInfo endpoint (manual override). HTTPS only.
issuer string Issuer identifier (manual override). HTTPS only.
audience string Required. Expected JWT aud claim
authFlow string "pkce" (default) or "implicit"
claims *OIDCClaimsSpec JWT claim mapping
getGroupsFromUserInfo *bool Fetch groups from UserInfo endpoint
getUsernameFromUserInfo *bool Fetch username from UserInfo endpoint
groupToRoleMapping map[string]string Map of IdP groups to comma-separated Neo4j roles
authParams string Extra authorization endpoint params (semicolon-separated)
tokenParams string Extra token endpoint params (semicolon-separated)

OIDCClaimsSpec

Field Type Description
username string JWT claim for Neo4j username (default: "sub")
groups string JWT claim for groups/roles

SecretKeyRef (TrustStore)

Used by the legacy singular spec.auth.trustStore field. New configurations should prefer the plural TrustedCASecret list at spec.trustedCASecrets; both fields are merged at reconcile time.

Field Type Description
name string Required. Name of Secret containing CA certificate (PEM format)
key string Key in the Secret containing the CA cert (default: "ca.crt")

RemoteAliasKeystoreSpec

Neo4j encrypts the credentials of remote database aliases before storing them in the system database, and refuses to create such an alias without a keystore:

Failed to create alias for remote database: the required setting(s)
[dbms.security.keystore.path, dbms.security.keystore.password] are missing
Field Type Description
secretRef string Required. A Secret in this namespace with two keys: keystore.p12 (the PKCS12 keystore) and password. The password reaches Neo4j as an environment variable sourced from this Secret, so it never appears in the operator-managed ConfigMap, and SHOW SETTINGS redacts it server-side.
keyName string Required. The keytool alias of the secret key inside the keystore — the -alias given to keytool -genseckey. Maps to dbms.security.key.name.

The operator does not generate the keystore. Create it with the JDK's keytool — ideally on the same Java version Neo4j runs, as Neo4j's documentation advises:

keytool -genseckey -keyalg aes -keysize 256 -storetype pkcs12 \
        -keystore keystore.p12 -alias my-key -storepass "$PASSWORD"

kubectl create secret generic remote-alias-keystore \
        --from-file=keystore.p12 --from-literal=password="$PASSWORD"

Every server mounts the same Secret, which satisfies Neo4j's requirement that the keystore file be identical across a cluster. Generating one per pod would give each server a different key and leave every alias readable by exactly one of them.

Rotating the keystore invalidates every existing remote alias

Neo4j encrypts each alias's credentials with this key. Changing the keystore or the key name makes all existing remote-alias credentials permanently unreadable and the aliases must be recreated, so rotation is a delete-and-recreate operation rather than an edit.

TrustedCASecret

References a Secret containing a PEM-encoded CA certificate to add to Neo4j's JVM truststore. Used by spec.trustedCASecrets.

Field Type Description
name string Required. Name of the Secret. Doubles as the keytool alias inside the JKS, so names must be unique across the list.
key string Key within the Secret. Defaults to "ca.crt" — the layout cert-manager-issued Secrets use.

The operator merges every entry into a JKS truststore at /truststore/truststore.jks (password changeit) using a truststore-init init container, then sets -Djavax.net.ssl.trustStore on the JVM. The JDK's default cacerts is used as the seed, so adding internal CAs does not remove trust in public CAs.

Example with cert-manager:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata: { name: oidc-tls }
spec:
  secretName: oidc-tls    # ← name to reference below
  issuerRef: { name: corp-ca, kind: ClusterIssuer }
  commonName: idp.corp.example.com
  dnsNames: [idp.corp.example.com]
---
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
spec:
  acceptLicenseAgreement: "eval"
  trustedCASecrets:
    - name: oidc-tls      # default key "ca.crt" matches cert-manager

For the less common case where a Neo4j SSL policy needs a CA at a specific filesystem path (e.g. dbms.ssl.policy.<name>.truststore_path), use spec.extraVolumes + spec.extraVolumeMounts instead — the operator does not parse policy config to wire up filesystem mounts automatically.

SecurityContextSpec

Field Type Description
podSecurityContext *corev1.PodSecurityContext Pod-level security settings
containerSecurityContext *corev1.SecurityContext Container-level security settings

TLSSpec

Field Type Description
mode string TLS mode: "cert-manager" (default) or "disabled". The validator rejects any other value.
issuerRef *IssuerRef cert-manager issuer reference. Required when mode: cert-manager. Validator rejects a cert-manager mode with no issuerRef.
certificateSecret string Reserved — no effect today. Intended for manual certificates, but no builder or controller reads it: the operator always uses the cert-manager-issued Secret {name}-tls-secret. A ValidationWarning event is raised whenever it is set, whatever mode is.
trustedCASecret string Secret containing a trusted CA certificate (key: ca.crt) for verifying Neo4j TLS connections. When omitted, the operator auto-discovers the CA from the cert-manager-generated Secret.
externalSecrets *ExternalSecretsConfig External Secrets configuration
duration *string Certificate duration (e.g., "2160h")
renewBefore *string Renewal window before expiry (e.g., "360h")
subject *CertificateSubject Certificate subject fields
usages []string Certificate usages (max 32 entries, each from the cert-manager key-usage list, e.g. digital signature, key encipherment, server auth, client auth). When set it replaces the operator defaults and must include both server auth and client auth (CEL rule and validator) — Neo4j needs both EKUs for its mutual-TLS posture. Omit to use the defaults.
strictPeerValidation *bool Intra-cluster TLS posture. Default: true (Neo4j's canonical production posture). When true, the operator emits dbms.ssl.policy.cluster.trust_all=false, client_auth=REQUIRE (mutual TLS), and verify_hostname=true, and projects the cert-manager Secret's ca.crt to /ssl/trusted/ca.crt as the trust anchor. When false, the operator emits trust_all=true + client_auth=NONE instead — Neo4j's own docs flag this as "debugging only, since it does not offer security." The opt-out exists for external issuers that do not populate ca.crt in their Secret output. See the TLS Configuration Guide for details.
additionalClusterTrustCAs []TrustedCASecret Peer clusters' CA certificates, each projected as its own file directly into /ssl/trusted/ (the cluster SSL policy's trust directory — Neo4j scans every file present there, not one fixed filename) alongside this cluster's own CA. Required for mutual TLS when replicating across clusters with different CAs — the normal case for crossClusterReplication — since trustedCASecrets above is a JVM-wide truststore that dbms.ssl.policy.cluster.* never reads. Must be set on both clusters, each trusting the other's CA. This list is the access-control list for the exposed port, so removing a decommissioned peer's CA is a revocation step, not housekeeping. Read only when mode is cert-manager — setting it without the mode is rejected rather than silently ignored. Only relevant when strictPeerValidation is true.

IssuerRef

Field Type Description
name string Required. Issuer resource name
kind string Issuer resource kind. Default: "ClusterIssuer". Use "Issuer" for namespace-scoped issuers, or any external issuer kind (e.g. "AWSPCAClusterIssuer", "VaultIssuer", "GoogleCASClusterIssuer")
group string API group of the issuer. Default: cert-manager.io. Set to the external issuer's API group when using third-party issuers (e.g. "awspca.cert-manager.io")

Examples:

# Standard cert-manager ClusterIssuer (default)
issuerRef:
  name: ca-cluster-issuer
  kind: ClusterIssuer

# Namespace-scoped Issuer
issuerRef:
  name: my-issuer
  kind: Issuer

# AWS Private CA (external issuer)
issuerRef:
  name: aws-pca-issuer
  kind: AWSPCAClusterIssuer
  group: awspca.cert-manager.io

# HashiCorp Vault
issuerRef:
  name: vault-issuer
  kind: VaultIssuer
  group: cert.cert-manager.io

CertificateSubject

Field Type Description
organizations []string Organization names
countries []string Country codes
organizationalUnits []string Organizational units
localities []string Localities
provinces []string Provinces/States

ExternalSecretsConfig

Requires External Secrets Operator to be installed separately — the operator only emits an ExternalSecret; ESO resolves it. A ClusterSecretStore works in both RBAC modes, because ESO (not this operator) performs the cluster-scoped read — see Operator modes.

Minimum External Secrets Operator version: v0.17.0

The operator emits apiVersion: external-secrets.io/v1. If you are on ESO < 0.17.0, upgrade ESO (its own migration guide covers the path).

Field Type Description
enabled bool Enable External Secrets integration
secretStoreRef *SecretStoreRef SecretStore or ClusterSecretStore reference
refreshInterval string Refresh interval (e.g., "1h"). Defaults to 1h when unset
data []ExternalSecretData External secret data mappings

SecretStoreRef

Field Type Description
name string SecretStore name
kind string "SecretStore" or "ClusterSecretStore"

ExternalSecretData

Field Type Description
secretKey string Target secret key
remoteRef *ExternalSecretRemoteRef Remote secret reference

ExternalSecretRemoteRef

Field Type Description
key string External secret key
property string Property within the secret
version string Secret version

AuraFleetManagementSpec

Enables integration with Neo4j Aura Fleet Management for monitoring this cluster from the Aura console. The fleet-management plugin is pre-bundled in all Neo4j Enterprise images (no internet access required).

Field Type Description
enabled bool Enable Aura Fleet Management integration (default: false)
tokenSecretRef *SecretKeyRef Reference to the Kubernetes Secret holding the Aura registration token (optional; registration deferred if omitted). Mutually exclusive with provision (enforced by a CEL rule: set at most one of provision or tokenSecretRef).
provision *AuraFleetProvisionSpec Let the operator register the deployment with Aura Fleet Manager and mint the registration token itself via the Aura API, instead of the console-wizard step. Beta / best-effort (the Fleet Manager request bodies are not published in the Aura OpenAPI spec). Mutually exclusive with tokenSecretRef.

AuraFleetProvisionSpec

Exactly one of providerConfigRef or credentialsSecretRef must be set (CEL rule: set exactly one of providerConfigRef or credentialsSecretRef).

Field Type Description
providerConfigRef corev1.LocalObjectReference AuraProviderConfig (credentials plus default organization/project) in the same namespace. Mutually exclusive with credentialsSecretRef.
credentialsSecretRef AuraCredentialsSecretRef Single-account shortcut when no AuraProviderConfig is used: name (required), clientIdKey (default clientId), clientSecretKey (default clientSecret). Mutually exclusive with providerConfigRef.
organizationId string Aura organization owning the fleet deployment; defaults to the AuraProviderConfig's defaultOrganizationId.
projectId string Aura project (API tenant_id); defaults to the AuraProviderConfig's defaultProjectId.
deploymentName string Name registered in Fleet Manager, max 30 characters. Default <namespace>-<name> truncated to 30 characters.
tokenSecretName string Secret the minted token is written to (key token), created in the CR's namespace and owned by the CR. Default <name>-aura-fleet-token.
tokenPolicy string CreateIfMissing (default) mints a token only when the Aura deployment has none — a token already registered successfully is never rotated, and the operator reports the problem instead. Rotate mints a fresh token whenever the Secret is missing or empty, invalidating any existing registration (the DBMS must re-register).
deletionPolicy string Aura-side behaviour on CR deletion: Orphan (default; leave it registered) or Delete (revoke the token and unregister the deployment).
managementPolicies []string Restricts which actions the operator may take against the Fleet Manager API; items are Observe, Create, Delete or * (default ["*"]).
collectTelemetry bool Mirror Aura's own view of the deployment's servers and databases into status.auraFleetManagement (default false). Kept separate from status.diagnostics, which is the operator's Bolt-derived view; telemetry failures never fail a reconcile.

Status fields (read-only, set by the operator):

Field Description
status.auraFleetManagement.registered true once fleetManagement.registerToken succeeded
status.auraFleetManagement.lastRegistrationTime Timestamp of last successful registration
status.auraFleetManagement.message Human-readable status or error detail
status.auraFleetManagement.provisioned true once the operator has registered a Fleet Manager deployment and stored a token for it (only meaningful with spec.provision)
status.auraFleetManagement.deploymentId Aura-assigned Fleet Manager deployment ID (also mirrored to the neo4j.com/external-fleet-deployment-id annotation, the authoritative idempotency guard)
status.auraFleetManagement.deploymentName Name the deployment is registered under in Aura
status.auraFleetManagement.tokenSecretName Secret the minted token was written to
status.auraFleetManagement.tokenCreationTime / tokenExpiryTime When Aura created / will expire the current deployment token (surfaced so an impending expiry is visible before it breaks registration)
status.auraFleetManagement.tokenAutoRotate Whether Aura will auto-renew the token on expiry
status.auraFleetManagement.servers[] / serverCount Aura's view of the servers reporting to this deployment (name, address, status, version, modeConstraint, lastPing, pluginVersion, licenseState, licenseType); a bounded summary — serverCount is the true total. Populated when provision.collectTelemetry is true
status.auraFleetManagement.databases[] / databaseCount Aura's view of the databases (name, serverId, currentStatus, role, writer, lastCommittedTxn, replicationLag, graphShards, propertyShards); one row per reporting server. Populated when provision.collectTelemetry is true
status.auraFleetManagement.telemetryError Why the last telemetry collection failed

Example:

auraFleetManagement:
  enabled: true
  tokenSecretRef:
    name: aura-fleet-token  # kubectl create secret generic aura-fleet-token --from-literal=token='<token>'
    key: token              # defaults to "token"

See Aura Fleet Management Guide for full setup instructions.


SecretKeyRef (Token)

Field Type Description
name string Required. Name of the Kubernetes Secret containing the registration token
key string Key within the Secret (default: "token")

CrossClusterReplicationSpec

Enables network-mode cross-cluster database replication (CCDR): the operator deploys a self-hosted TCP proxy (HAProxy, mode tcp, pure L4 passthrough — it never terminates or inspects TLS) that exposes this cluster's tx-shipping port (6000) externally via one LoadBalancer Service, and overrides server.cluster.advertised_address per-server to the proxy's external endpoint. server.cluster.raft.advertised_address (7000) and server.routing.advertised_address (7688) are left untouched — RAFT leader election never depends on this load balancer.

Opt-in tradeoff, stated plainly: enabling this means intra-cluster secondary catch-up traffic (which shares port 6000 with cross-cluster replica traffic) also routes through the load balancer, adding latency and LB-dependent cost proportional to write volume.

Field Type Description
enabled bool Deploy the proxy and override advertised addresses. Default false.
loadBalancerInternal *bool Keep the proxy's Service on a private/internal load balancer where the cloud provider supports it. Default true — this design has no DNS split to keep intra-cluster traffic off the load balancer, so an internal LB is the main available mitigation against public exposure.
annotations map[string]string Passed through to the proxy's Service, for cloud-specific load balancer tuning (e.g. the internal-LB annotation key, which differs per cloud provider).

Status fields (read-only, set by the operator):

Field Description
status.crossClusterReplication.ready true once the proxy's LoadBalancer Service has an assigned hostname/IP and every server's advertised address has been updated to route through it
status.crossClusterReplication.loadBalancerHostname External hostname or IP assigned to the proxy's Service
status.crossClusterReplication.addresses Ready-to-paste list for a downstream Neo4jReplicaDatabase's spec.source.addresses — one entry per server ordinal, <lb-hostname>:<port>. Only one entry is actually required on the downstream side (Neo4j redirects to the rest), but the full list is published so any entry can be used.

Example — enable on the upstream cluster, then feed its status into a downstream Neo4jReplicaDatabase in another Kubernetes cluster:

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata: { name: prod }
spec:
  acceptLicenseAgreement: "eval"
  crossClusterReplication:
    enabled: true
  tls:
    mode: cert-manager
    issuerRef: { name: ca-cluster-issuer, kind: ClusterIssuer }
    additionalClusterTrustCAs:
      - name: dr-cluster-ca   # the downstream cluster's CA Secret, copied here
---
# status.crossClusterReplication.addresses on `prod`, once ready, becomes
# spec.source.addresses on the downstream Neo4jReplicaDatabase — see
# docs/api_reference/neo4jreplicadatabase.md.

PropertyShardingSpec

Configures property sharding for horizontal scaling of large datasets. Property sharding separates graph structure from properties, distributing properties across multiple databases for better scalability. Available in Neo4j 2025.12+ Enterprise.

Field Type Description
enabled bool Enable property sharding for this cluster (default: false)
config map[string]string Advanced property sharding configuration

System Requirements (validated by operator):

  • Neo4j Version: 2025.12+ Enterprise
  • Minimum Servers: 2 servers (3+ recommended for HA graph shard primaries)
  • Memory: 4GB minimum, 8GB+ recommended per server
  • CPU: 1+ core minimum, 2+ cores recommended per server
  • Authentication: Admin secret required
  • Storage: Persistent storage class required

Required Configuration (automatically applied when enabled):

  • internal.dbms.sharded_property_database.enabled: "true"
  • internal.dbms.sharded_property_database.allow_external_shard_access: "false"

Sharding does not set db.query.default_language: shard sub-databases inherit the parent database's language, which the operator sets to Cypher 25 on every sharded database. The server default is serverDefaultCypherLanguage's.

Performance Tuning Options:

propertySharding:
  enabled: true
  config:
    # Transaction log retention (critical for shard sync)
    db.tx_log.rotation.retention_policy: "14 days"

    # Property synchronization interval
    internal.dbms.sharded_property_database.property_pull_interval: "10ms"

    # Memory optimization
    server.memory.heap.max_size: "12G"
    server.memory.pagecache.size: "6G"

    # Connection pooling for cross-shard queries
    server.bolt.thread_pool_min_size: "10"
    server.bolt.thread_pool_max_size: "100"

Resource Recommendations:

Development:

resources:
  requests:
    memory: 4Gi    # Absolute minimum for dev/test
    cpu: 2000m     # 2 cores for cross-shard queries
  limits:
    memory: 8Gi    # Recommended for production
    cpu: 2000m

Production:

resources:
  requests:
    memory: 4Gi    # Minimum requirement
    cpu: 2000m     # Cross-shard performance
  limits:
    memory: 8Gi    # Recommended for production
    cpu: 4000m     # Handle peak loads

Validation Errors:

Error Cause Resolution
property sharding requires Neo4j 2025.12+ Old Neo4j version Upgrade to 2025.12+ Enterprise
spec.topology.servers in body should be greater than or equal to 2 Invalid server count Increase server count to 2+ (3+ recommended for HA)
property sharding requires minimum 4GB memory Insufficient memory Increase memory to 8GB+ (recommended)
property sharding requires minimum 1 CPU core Insufficient CPU Increase CPU to 2+ cores (recommended)

For detailed configuration, see the Property Sharding Guide.

MonitoringSpec

Query performance monitoring and analytics configuration.

Field Type Description
enabled bool Enable query monitoring (default: true)
slowQueryThreshold string Slow query threshold — maps to db.logs.query.threshold (default: "5s")
explainPlan bool Enable query plan explanation in logs. Has performance impact — recommended false in production (default: false)
queryLogLevel string Query log verbosity: OFF, INFO, or VERBOSE (default: "INFO")
obfuscateLiterals bool Obfuscate literal values in query logs to protect sensitive data (default: false)
metricsFilter string Glob pattern for which Neo4j metrics to expose, e.g. "*" for all (maps to server.metrics.filter)
metricsPrefix string Custom prefix for Neo4j metric names (maps to server.metrics.prefix)
sampling *QuerySamplingConfig Query sampling configuration
metricsExport *QueryMetricsExportConfig Metrics export configuration
logs *MonitoringLogsSpec Neo4j's own logs on the containers' standard output, as JSON, for a node-level log collector. Applies whether or not enabled is true.

MonitoringLogsSpec

Neo4j writes query.log, security.log and debug.log to files under /logs; only neo4j.log reaches the container's standard output. Listing a log here also writes it to standard output, one JSON object per line, where an OpenTelemetry Collector or another node-level log collector picks it up with the pod's metadata. The files keep their usual formats. See OpenTelemetry.

Field Type Description
stdout []string Logs to write to standard output: query (Neo4j's QueryLogJsonLayout), security (StructuredJsonLayout), debug (StructuredLayoutWithMessage). A set of up to three.

The operator renders the log configuration (server-logs.xml) into the ConfigMap and points server.logs.config at the mounted copy:

  • Turning it on or off restarts each server once. server.logs.config is read at startup; a ConfigNeedsRestart event says so.
  • Changing the list afterwards restarts nothing. Neo4j re-reads the file it is pointed at every 30 seconds, and the kubelet updates the mount, so the change takes effect usually within two minutes.
  • spec.config may not set server.logs.config while a log is listed; validation refuses it.

QuerySamplingConfig

Query sampling configuration for performance monitoring.

Field Type Description
rate string Sampling rate as a decimal from 0 to 1 inclusive, pattern ^(0(\.\d+)?\|1(\.0+)?)$ (e.g. "0.5", "1.0", "0")
maxQueriesPerSecond int32 Maximum queries to sample per second

QueryMetricsExportConfig

Metrics export configuration for query monitoring.

Field Type Description
prometheus bool Export to Prometheus
customEndpoint string Export to custom endpoint
interval string Export interval

PlacementConfig

Advanced placement and scheduling configuration.

Field Type Description
topologySpread *TopologySpreadConfig Topology spread constraints
antiAffinity *PodAntiAffinityConfig Pod anti-affinity rules
nodeSelector map[string]string Reserved — no effect today. Never read by the topology scheduler; use the top-level spec.nodeSelector. A ValidationWarning event is raised when it is set.
requiredDuringScheduling bool Reserved — no effect today. Never read; use antiAffinity.type: required or topology.enforceDistribution for hard placement. A ValidationWarning event is raised when it is true.

TopologySpreadConfig

Controls how servers are distributed across cluster topology.

Field Type Description
enabled bool Enable topology spread constraints
topologyKey string Topology domain (e.g., "topology.kubernetes.io/zone")
maxSkew int32 Maximum allowed imbalance between domains
whenUnsatisfiable string Action when constraints can't be satisfied
minDomains *int32 Minimum number of eligible domains

PodAntiAffinityConfig

Prevents servers from being scheduled on the same nodes/zones.

Field Type Description
enabled bool Enable anti-affinity rules
topologyKey string Anti-affinity topology domain
type string Constraint type: "required" or "preferred"

ServiceSpec

Configures how Neo4j is exposed outside the Kubernetes cluster.

Field Type Description
type string Service type: "ClusterIP", "NodePort", "LoadBalancer" (default: "ClusterIP")
annotations map[string]string Service annotations (e.g., for cloud load balancer configuration)
loadBalancerIP string Static IP for LoadBalancer service (cloud provider specific)
loadBalancerSourceRanges []string IP ranges allowed to access LoadBalancer
externalTrafficPolicy string External traffic policy: "Cluster" or "Local"
dnsName string Public DNS hostname (e.g. "neo4j.example.com"). Surfaces as the external-dns.alpha.kubernetes.io/hostname annotation on the front-facing Service and (when enabled) the Ingress so external-dns creates the matching cloud DNS record; also added to the cert-manager Certificate SAN list when spec.tls is enabled. See the External Access guide.
ingress IngressSpec Ingress configuration
route RouteSpec OpenShift Route configuration

AuditSpec

Compliance-oriented logging configuration. Neo4j 5.x / 2025.x has no unified dbms.security.audit.* block (those were 4.x keys, removed) — "audit logging" in modern Neo4j is the combination of security.log (controlled by dbms.security.*) and query.log (controlled by db.logs.query.*). This spec exposes the audit-relevant subset of those keys as typed fields.

Field Type Neo4j Config Key Description
enabled bool — Master flag. When true AND obfuscateQueryLiterals is unset, the operator defaults the emitted obfuscation value to true (secure-by-default). Default false.
logSuccessfulAuthentication *bool dbms.security.log_successful_authentication When false, only FAILED logins appear in security.log. Unset → Neo4j default (true) applies.
obfuscateQueryLiterals *bool db.logs.query.obfuscate_literals Redact literal values in query.log. Strongly recommended for PCI / HIPAA / GDPR. Unset + enabled=true → defaults to true. Doesn't redact node labels, relationship types, or property keys.
parameterLogging *bool db.logs.query.parameter_logging_enabled Include parameter VALUES in query.log. Set false when parameter values are sensitive. Unset → Neo4j default (true) applies.

Overlap with MonitoringSpec: both touch db.logs.query.obfuscate_literals. When set on both, spec.audit wins — the operator emits monitoring first, then audit, and Neo4j's last-write-wins config semantics give audit the final value. User spec.config overrides (appended last) still win over both.

NetworkPolicySpec

Controls operator emission of a Kubernetes NetworkPolicy that hardens ingress to the Neo4j server pods. Most importantly, closes the backup port (6362) to non-backup pods.

Field Type Description
enabled bool When true, the operator emits a NetworkPolicy named <cluster>-server-netpol (cluster) or <standalone>-standalone-netpol (standalone). Public client ports (7474/7473/7687) remain open to any pod; intra-cluster ports (6000/7000/7688) are restricted to peer servers; backup port (6362) is restricted to operator-managed backup pods. Default false.
allowReplicasFrom []NetworkPolicyPeerCluster Opt-in: additively admits named downstream Neo4jEnterpriseCluster(s) on port 6000 only (never RAFT/routing), for a same-Kubernetes-cluster network-mode CCDR replica that would otherwise be blocked by the peer-restriction rule (which only admits this cluster's own neo4j.com/cluster label). Nothing is admitted unless listed here. Irrelevant to backup-mode replication, which has no network coupling to restrict.

NetworkPolicyPeerCluster

Field Type Description
name string Required. Name of the downstream Neo4jEnterpriseCluster to admit.
namespace string Namespace of the downstream Neo4jEnterpriseCluster. Defaults to this cluster's own namespace when omitted.

Scoped by both a podSelector (neo4j.com/cluster: <name>) and a namespaceSelector (Kubernetes' well-known kubernetes.io/metadata.name label) — a podSelector alone would match a same-named cluster in any namespace.

CNI prerequisite: NetworkPolicy is enforced only by Calico, Cilium, Antrea, Weave, and most managed offerings. Flannel does NOT enforce NetworkPolicy — enabling this on a flannel cluster creates the resource but has no effect on traffic. See Network Security for details.

IngressSpec

Configures an Ingress resource for HTTP(S) access to Neo4j Browser.

Field Type Description
enabled bool Enable Ingress creation
className string Ingress class name (e.g., "nginx")
annotations map[string]string Ingress annotations
host string Hostname for the Ingress
tlsSecretName string TLS secret name for Ingress

RouteSpec

Field Type Description
enabled bool Enable OpenShift Route creation
host string Hostname for the Route (optional)
path string Path for the Route (default: "/")
annotations map[string]string Route annotations
tls *RouteTLSSpec TLS settings for the Route
targetPort int32 Target service port (default: 7474)

RouteTLSSpec

Field Type Description
termination string TLS termination: "edge", "reencrypt", "passthrough"
insecureEdgeTerminationPolicy string "None", "Allow", "Redirect"
secretName string Secret containing certificate (reencrypt/passthrough)

MCPServerSpec

Optional MCP server deployment for the cluster using the official mcp/neo4j image (github.com/neo4j/mcp). Requires the APOC plugin for the get-schema tool.

HTTP transport (default): no credentials stored in the pod — each client request carries a Basic Auth or Bearer token Authorization header. A Service is created at <name>-mcp:8080; the endpoint path is /mcp (fixed).

STDIO transport: NEO4J_USERNAME and NEO4J_PASSWORD are injected from the admin secret (or a custom secret via spec.mcp.auth). No Service is created.

For client configuration, see the MCP Client Setup Guide.

Field Type Description
enabled bool Enable MCP server deployment (default: false)
image *ImageSpec MCP server image. Defaults to mcp/neo4j:latest. Pin a version with image.tag.
transport string Transport mode: "http" (default) or "stdio"
readOnly bool Disable the write-cypher tool when true (default: true)
database string Default Neo4j database for MCP queries
schemaSampleSize *int32 Schema sampling size for get-schema. Use -1 to sample entire graph.
telemetry *bool Enable anonymous usage telemetry sent to Neo4j. When unset, uses the server default (true). Set to false to opt out.
logLevel string Log verbosity: debug, info, notice, warning, error, critical, alert, emergency
logFormat string Log output format: text (default) or json
http *MCPHTTPConfig HTTP transport configuration (only used when transport: http)
auth *MCPAuthSpec Override Neo4j credentials for STDIO transport. Ignored in HTTP mode (credentials come per-request from the client).
replicas *int32 Number of MCP pod replicas (default: 1). Only meaningful for HTTP.
resources *corev1.ResourceRequirements Resource requirements for MCP pods
env []corev1.EnvVar Extra environment variables. Operator-managed vars (NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD, NEO4J_TRANSPORT_MODE, NEO4J_MCP_HTTP_*, etc.) are silently ignored.
securityContext *SecurityContextSpec Pod/container security context overrides

MCPHTTPConfig

HTTP transport configuration. Only applied when spec.mcp.transport: http.

Field Type Description
host string HTTP bind host (default: 0.0.0.0)
port int32 HTTP bind port. Default: 8080 without TLS, 8443 with TLS.
tls *MCPTLSSpec Optional container-level TLS. When unset, handle TLS at the Ingress layer instead.
authHeaderName string Name of the HTTP header carrying credentials (default: Authorization). Override when a proxy rewrites the standard header.
service *MCPServiceSpec Kubernetes Service and Ingress/Route exposure settings

MCPTLSSpec

Enables container-level TLS on the mcp/neo4j HTTP server by mounting a Kubernetes TLS secret and injecting NEO4J_MCP_HTTP_TLS_ENABLED=true with the cert/key file paths. For most deployments, Ingress-level TLS termination is simpler.

Field Type Description
secretName string Required. Name of a Kubernetes TLS secret (type: kubernetes.io/tls) containing the certificate and private key.
certKey string Key in the secret for the certificate file (default: tls.crt)
keyKey string Key in the secret for the private key file (default: tls.key)

MCPServiceSpec

Field Type Description
type string Service type: "ClusterIP" (default), "NodePort", "LoadBalancer"
annotations map[string]string Service annotations
loadBalancerIP string Static LoadBalancer IP
loadBalancerSourceRanges []string Allowed source ranges
externalTrafficPolicy string External traffic policy: "Cluster" or "Local"
port int32 Service port for MCP HTTP (default: same as container port)
ingress *IngressSpec Ingress configuration. Path is always /mcp (fixed by the official image).
route *RouteSpec OpenShift Route configuration

MCPAuthSpec

Overrides the Neo4j credentials injected into the MCP pod. Only effective for STDIO transport. In HTTP mode the server authenticates per-request using the client's Authorization header.

Field Type Description
secretName string Secret containing username/password keys. Defaults to the cluster admin secret when unset.
usernameKey string Username key name (default: username)
passwordKey string Password key name (default: password)

UpgradeStrategySpec

Configures how the operator handles Neo4j version upgrades.

Field Type Description
strategy string Upgrade strategy: "RollingUpgrade" (default) or "Recreate"
preUpgradeHealthCheck bool Enable health validation before upgrade (default: true)
maxUnavailableDuringUpgrade *int32 Accepted but has no effect: the operator restarts one server at a time, as Neo4j's rolling upgrade requires, and waits for its databases to catch up before the next. A value other than 1 (the default) raises a ValidationWarning
upgradeTimeout string Timeout for the entire upgrade process (default: "30m")
postUpgradeHealthCheck bool Enable health validation after upgrade (default: true)
healthCheckTimeout string Timeout for health checks (default: "5m")
stabilizationTimeout string Wait time for cluster stabilization (default: "3m")
autoPauseOnFailure bool Pause upgrade on failure for manual intervention (default: true)

Status

The Neo4jEnterpriseClusterStatus represents the observed state of the cluster.

Field Type Description
phase string Cluster phase: "Initializing", "Forming", "Ready", "Degraded" (formed and serving with a majority of its servers, one or more unavailable for longer than the grace period — see Server availability), "Expanding" (storage expansion in progress), "Paused" (upgrade paused after a failure), "Failed"
ready bool true only in phase Ready; false in phase Degraded, though the cluster still serves
message string Human-readable status message
conditions []metav1.Condition Cluster conditions (e.g. Ready, Degraded, ClusterFormed, ServersHealthy, DatabasesHealthy, ServersPendingDrain) — see Conditions
replicas *ReplicaStatus Server counts: servers (desired) and ready
endpoints EndpointStatus Service endpoints
version string Current Neo4j version
upgradeStatus *UpgradeStatus Detailed upgrade progress information
lastUpgradeTime *metav1.Time When the last upgrade was performed
propertyShardingReady bool Whether property sharding is configured and ready (Neo4j 2025.12+)
effectiveCypherLanguage string The server default Cypher version the operator resolved (see spec.serverDefaultCypherLanguage), recorded once so an unset spec never changes meaning under a running cluster
auraFleetManagement object State of the Aura Fleet Management integration (when spec.auraFleetManagement.enabled=true)
observedGeneration int64 Last observed generation
diagnostics *DiagnosticsStatus Live diagnostics, collected once the cluster has formed (also while it is degraded) unless spec.monitoring.enabled: false.
crossClusterReplication *CrossClusterReplicationStatus State of the network-mode CCDR exposure proxy, when spec.crossClusterReplication.enabled is true.
internalAddresses []string Ready-to-paste <pod-fqdn>:6000 list for a downstream Neo4jReplicaDatabase on this same Kubernetes cluster (source.upstreamClusterRef, or pasted directly into source.addresses). Always populated — no proxy, no LoadBalancer needed — but not routable from a genuinely separate Kubernetes cluster; use crossClusterReplication for that.

EndpointStatus

Service endpoints and connection information populated by the controller on every status update. Schema is shared between Neo4jEnterpriseCluster and Neo4jEnterpriseStandalone; both controllers resolve URLs against the {name}-client ClusterIP (on a standalone, {name}-service also resolves but is deprecated; use {name}-client).

Field Type Description
bolt string Bolt protocol endpoint (e.g. bolt+s://<service>:7687 when TLS is enabled, bolt://... when disabled)
http string HTTP endpoint for Neo4j Browser
https string HTTPS endpoint for Neo4j Browser
internal InternalEndpoints In-cluster service FQDNs (headless + client; cluster only)
connectionExamples ConnectionExamples Ready-to-paste connection strings driven by spec.service.type (ClusterIP / NodePort / LoadBalancer)

ReplicaStatus

Field Type Description
servers int32 Number of servers
ready int32 Number of ready servers

InternalEndpoints

Field Type Description
headless string Headless service FQDN used for pod-level discovery
client string Client service FQDN used for client connections

ConnectionExamples

Connection-string snippets generated from spec.service.type and the LoadBalancer-assigned external IP (when applicable). On a LoadBalancer service the external IP is filled in once the cloud provider's controller has assigned one; until then a <external-ip> placeholder is substituted. For NodePort the bolt port and HTTPS port appear as <bolt-node-port> / <http-node-port> placeholders since those are dynamically assigned and visible via kubectl get svc.

Field Type Description
portForward string kubectl port-forward command (always available)
browserURL string Neo4j Browser URL
boltURI string Bolt connection URI (uses bolt+ssc:// when TLS is enabled, since cert-manager-issued certs typically aren't trusted by external clients)
neo4jURI string Neo4j routing-driver URI
pythonExample string Python driver connection snippet
javaExample string Java driver connection snippet

UpgradeStatus

Detailed upgrade progress tracking.

Field Type Description
phase string Upgrade state-machine phase (schema enum Pending, Staging, InProgress, Rolling, Stabilizing, Verifying, Paused, Completed, Failed): Staging (target pod template applied, pod restarts frozen) → Rolling (servers restarted one at a time, highest ordinal first) → Stabilizing (health, consensus and replication gate) → Verifying (per-server version check) → Completed. Paused and Failed are terminal until operator/user action. InProgress is resumed as Staging; Pending is reserved
startTime *metav1.Time When the upgrade started
stepStartTime *metav1.Time Anchor for the per-step timeout (upgradeTimeout per Rolling step, stabilizationTimeout, healthCheckTimeout); reset on every phase transition and partition advance
phaseStartTime *metav1.Time When the current phase began; reset only when the phase changes (unlike stepStartTime, which also restarts on every partition advance inside Rolling). It is the anchor neo4j_operator_upgrade_duration_seconds is measured from
currentPartition *int32 StatefulSet RollingUpdate partition most recently applied; servers with ordinal >= partition have been rolled to the target version. Used to resume after an operator restart
completionTime *metav1.Time When the upgrade completed
currentStep string Current upgrade step description
previousVersion string Version before upgrade
targetVersion string Version being upgraded to
progress *UpgradeProgress Upgrade progress statistics
message string Additional upgrade details
lastError string Last error encountered during upgrade

UpgradeProgress

Upgrade progress across servers.

Field Type Description
total int32 Total number of servers to upgrade
upgraded int32 Number of servers successfully upgraded
inProgress int32 Number of servers currently being upgraded
pending int32 Number of servers pending upgrade
servers *NodeUpgradeProgress Server upgrade details

NodeUpgradeProgress

Server-specific upgrade progress.

Field Type Description
total int32 Total number of servers
upgraded int32 Number of servers successfully upgraded
inProgress int32 Number of servers currently being upgraded
pending int32 Number of servers pending upgrade
currentLeader string Current leader server

DiagnosticsStatus

Live diagnostics collected from SHOW SERVERS and SHOW DATABASES once the cluster has formed, including while it is degraded with a server down. On by default; spec.monitoring.enabled: false turns it off. Collected on every reconcile and written to status when something in them changes, or at least every 5 minutes (see lastCollected).

Field Type Description
users []UserDiagnosticInfo Users currently in the system database (SHOW USERS): user, roles, suspended, homeDatabase. A bounded summary — see userCount.
userCount int Total number of users observed, even when users is truncated.
roles []RoleDiagnosticInfo Roles currently in the system database (SHOW ROLES): role, immutable. A bounded summary — see roleCount.
roleCount int Total number of roles observed, even when roles is truncated.
servers []ServerDiagnostic SHOW SERVERS results. Each entry has name, address, state, health, hostingDatabases.
servers[].name string Server display name.
servers[].address string Bolt address of the server.
servers[].state string Lifecycle state: Enabled, Cordoned, Deallocating.
servers[].health string Health status: Available or Unavailable.
servers[].hostingDatabases int Number of databases currently hosted on this server.
databases []DatabaseDiagnostic SHOW DATABASES results. Each entry has name, status, requestedStatus, role, default, plus type (system, standard, composite, graph shard/property shard on 2025.12+, replica on 2026.08+), access (read-write/read-only), writer, lastCommittedTxn and replicationLag (for a cross-cluster replica, the data loss that promoting now would make permanent).
databases[].name string Database name.
databases[].status string Current status: online, offline, quarantined.
databases[].requestedStatus string Desired status as requested by the operator.
databases[].role string Role on the last-contacted server: primary, secondary.
databases[].default bool Whether this is the cluster's default database.
lastCollected string (RFC3339) When the diagnostics were last written. They are collected on every reconcile but written only when something in them changes (a server, a database, the users, the roles, an error) or at least every 5 minutes, so this is at most 5 minutes old while the cluster is reconciling. The databases' lastCommittedTxn and replicationLag move with every write and do not count as a change: they are a snapshot from the last write.
collectionError string Error message from the last failed collection; empty on success.

Conditions

The operator maintains the following condition types on status.conditions (standard metav1.Condition):

Condition Type True when False when Unknown when
Degraded The cluster has formed and one or more of its servers is unavailable (reason ServersUnavailable; the message names them). Its lastTransitionTime is when the shortfall began Every server is available (reason AllServersAvailable) n/a — absent until the cluster first forms
ClusterFormed The cluster has formed at least once. Never set back to False n/a n/a — absent until the cluster first forms
ServersHealthy All servers are state=Enabled and health=Available Any server is Cordoned, Deallocating, or Unavailable Diagnostics cannot be collected (cluster not formed yet, or Bolt unreachable)
DatabasesHealthy All user databases have status=online Any database has requestedStatus=online but status≠online Diagnostics cannot be collected (cluster not formed yet, or Bolt unreachable)
ServersPendingDrain Set during a scale-down: the cluster still has Neo4j servers registered beyond spec.topology.servers (removed servers not yet deallocated and dropped), so databases may be under-replicated on them The removed servers have been drained and dropped n/a
RestartPending One or more servers have not restarted since the operator added a setting it applies at each server's next restart instead of restarting for it (reason SettingsAwaitRestart; the message names the servers and settings). Today: dbms.cluster.raft.async_channel_acquisition_enabled on 5.26.29+ (see 5.26: writes while a server is stopped), and dbms.security.tls_reload_enabled on CalVer 2025.03+ with cert-manager TLS (see TLS certificate rotation). Restart the named pods, or let it happen on their next restart Every server runs those settings (reason NoSettingsAwaitRestart) n/a — absent when nothing is applied at restart (CalVer, or the setting is set in spec.config)
CrossClusterProxySecure The cluster SSL policy requires a peer certificate on the port the CCDR proxy publishes strictPeerValidation is false, so trust_all=true accepts any peer certificate — the exposed port is encrypted but not authenticated. (A cluster with no TLS at all cannot reach this state: the proxy is rejected at validation.) n/a — the condition is removed entirely when the proxy is disabled

Note: The system database is excluded from the DatabasesHealthy check because it has special internal lifecycle behavior.

Server availability

A cluster is Forming until every server has joined it once. After that, a missing server — a pod restart, an OOM kill, a node drain — is an outage of a formed cluster, and the cluster is judged on whether it is still serving:

Situation Phase Ready condition Degraded condition Dependents¹
Every server available Ready True False proceed
A majority available, short for less than the grace period Ready True True proceed
A majority available, short for longer than the grace period Degraded False (ClusterDegraded) True proceed
Half or fewer available Forming Unknown True pause
First formation, a scale-up, or a rollout of the server StatefulSet Forming Unknown unchanged pause

¹ Neo4jUser, Neo4jRole, Neo4jRoleBinding, Neo4jAuthRule, Neo4jDatabase, Neo4jShardedDatabase, Neo4jDatabaseAlias, Neo4jCompositeDatabase, Neo4jReplicaDatabase, Neo4jReplicaPromotion and Neo4jBackup. Whether a statement can run on the servers that are up is Neo4j's call, reported on that resource. Operations that restart every server — a rolling image upgrade, a Neo4jPlugin install — wait until every server is back.

The grace period defaults to 5 minutes and is set with the operator flag --server-unavailable-grace (Helm: serverUnavailableGrace). Raise it if your servers take longer than that to restart and rejoin. Entering Degraded raises one ClusterDegraded Warning event; losing a majority raises one ClusterQuorumLost Warning. ServersHealthy, DatabasesHealthy and the neo4j_operator_server_health metric keep updating throughout.

Examples

Basic Cluster

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: basic-cluster
spec:
  acceptLicenseAgreement: "eval"
  image:
    repo: neo4j  # Note: field name is 'repo', not 'repository'
    tag: "5.26-enterprise"
  topology:
    servers: 3  # Creates StatefulSet basic-cluster-server with 3 replicas
  storage:
    className: standard
    size: 10Gi
  auth:
    adminSecret: neo4j-admin-secret  # Note: field name is 'adminSecret'
  resources:
    requests:
      cpu: "1"
      memory: 4Gi
    limits:
      cpu: "2"
      memory: 8Gi

Cluster with Server Role Constraints

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: role-constrained-cluster
spec:
  acceptLicenseAgreement: "eval"
  image:
    repo: neo4j
    tag: "5.26-enterprise"
  topology:
    servers: 5
    # Global constraint: all servers default to any role
    serverModeConstraint: NONE
    # Per-server role hints (overrides global constraint)
    serverRoles:
      - serverIndex: 0
        modeConstraint: PRIMARY    # Server-0: only primary databases
      - serverIndex: 1
        modeConstraint: PRIMARY    # Server-1: only primary databases
      - serverIndex: 2
        modeConstraint: SECONDARY  # Server-2: only secondary databases
      - serverIndex: 3
        modeConstraint: SECONDARY  # Server-3: only secondary databases
      - serverIndex: 4
        modeConstraint: NONE       # Server-4: any database mode
    # Advanced placement for high availability
    placement:
      topologySpread:
        enabled: true
        topologyKey: topology.kubernetes.io/zone
        maxSkew: 1
        whenUnsatisfiable: DoNotSchedule
      antiAffinity:
        enabled: true
        topologyKey: kubernetes.io/hostname
        type: required
    availabilityZones:
      - us-east-1a
      - us-east-1b
      - us-east-1c
    enforceDistribution: true
  storage:
    className: fast-ssd
    size: 50Gi
  auth:
    adminSecret: neo4j-admin-secret

Cluster with Query Monitoring

Backups are configured separately via the Neo4jBackup CRD — see the next example.

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: monitored-cluster
spec:
  acceptLicenseAgreement: "eval"
  image:
    repo: neo4j
    tag: "5.26-enterprise"
  topology:
    servers: 3  # Creates monitored-cluster-server StatefulSet
  storage:
    className: fast-ssd
    size: 50Gi
  auth:
    adminSecret: neo4j-admin-secret
  # Enhanced query monitoring
  monitoring:
    enabled: true
    slowQueryThreshold: "1s"
    queryLogLevel: "INFO"
    obfuscateLiterals: true
    sampling:
      rate: "0.1"
      maxQueriesPerSecond: 100
    metricsExport:
      prometheus: true
      interval: "30s"

Create Scheduled Backup (Separate Resource)

# Backups are managed via the Neo4jBackup CRD
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: daily-cluster-backup
spec:
  instanceRef: monitored-cluster   # the Neo4jEnterpriseCluster (or Standalone) to back up
  allDatabases: true               # or `database: <name>` for a single database
  storage:
    type: s3
    bucket: neo4j-backups
    path: daily/
  schedule: "0 2 * * *"  # Daily at 2 AM
  retention:
    maxAge: "30d"
    maxCount: 30
  options:
    compress: true
    backupType: FULL

Cluster with LoadBalancer Service

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: public-cluster
spec:
  acceptLicenseAgreement: "eval"
  image:
    repo: neo4j
    tag: "5.26-enterprise"
  topology:
    servers: 5  # Creates public-cluster-server StatefulSet with 5 replicas
  storage:
    className: standard
    size: 20Gi
  auth:
    adminSecret: neo4j-admin-secret
  # LoadBalancer service configuration
  service:
    type: LoadBalancer
    annotations:
      # AWS NLB example
      service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
      service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    loadBalancerSourceRanges:
      - "10.0.0.0/8"      # Corporate network
      - "172.16.0.0/12"   # VPN range
    externalTrafficPolicy: Local  # Preserve client IPs

Cluster with Ingress

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: ingress-cluster
spec:
  acceptLicenseAgreement: "eval"
  image:
    repo: neo4j
    tag: "5.26-enterprise"
  topology:
    servers: 3  # Creates ingress-cluster-server StatefulSet
  storage:
    className: fast-ssd
    size: 20Gi
  auth:
    adminSecret: neo4j-admin-secret
  # TLS configuration
  tls:
    mode: cert-manager
    issuerRef:
      name: letsencrypt-prod
      kind: ClusterIssuer
  # Ingress configuration
  service:
    ingress:
      enabled: true
      className: nginx
      host: neo4j.example.com
      tlsSecretName: neo4j-tls
      annotations:
        cert-manager.io/cluster-issuer: letsencrypt-prod
        nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"

Management Commands

Basic Operations

# Create a cluster
kubectl apply -f cluster.yaml

# List clusters
kubectl get neo4jenterprisecluster

# Get cluster details
kubectl describe neo4jenterprisecluster my-cluster

# Check cluster status
kubectl get neo4jenterprisecluster my-cluster -o yaml

# Port forward for local access
kubectl port-forward svc/my-cluster-client 7474:7474 7687:7687

Cluster Operations

# Scale cluster (change server count)
kubectl patch neo4jenterprisecluster my-cluster -p '{"spec":{"topology":{"servers":5}}}'

# Update Neo4j version
kubectl patch neo4jenterprisecluster my-cluster -p '{"spec":{"image":{"tag":"2025.01.0-enterprise"}}}'

# Check cluster health
kubectl exec my-cluster-server-0 -- cypher-shell -u neo4j -p password "SHOW SERVERS"

# Monitor server pods
kubectl get pods -l neo4j.com/cluster=my-cluster
kubectl logs my-cluster-server-0 -c neo4j

Usage Patterns

Multi-Database Architecture

# 1. Create cluster with role-optimized servers
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: multi-db-cluster
spec:
  acceptLicenseAgreement: "eval"
  topology:
    servers: 6
    serverRoles:
      - serverIndex: 0
        modeConstraint: PRIMARY    # Dedicated for write workloads
      - serverIndex: 1
        modeConstraint: PRIMARY
      - serverIndex: 2
        modeConstraint: SECONDARY  # Dedicated for read workloads
      - serverIndex: 3
        modeConstraint: SECONDARY
      - serverIndex: 4
        modeConstraint: NONE       # Mixed workloads
      - serverIndex: 5
        modeConstraint: NONE
  # ... other configuration

---
# 2. Create databases with specific topologies
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jDatabase
metadata:
  name: user-database
spec:
  clusterRef: multi-db-cluster
  name: users
  topology:
    primaries: 2    # Uses servers 0-1 (PRIMARY constraint)
    secondaries: 2  # Uses servers 2-3 (SECONDARY constraint)

---
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jDatabase
metadata:
  name: analytics-database
spec:
  clusterRef: multi-db-cluster
  name: analytics
  topology:
    primaries: 1    # Uses server 4 or 5 (NONE constraint)
    secondaries: 3  # Uses remaining servers

Development vs Production

The dev-cluster and prod-cluster manifests below omit the required spec.image (and auth, where unset) for brevity — add them as in the examples above.

# Development cluster (minimal resources)
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: dev-cluster
spec:
  acceptLicenseAgreement: "eval"
  topology:
    servers: 2  # Minimum for clustering
  storage:
    className: standard
    size: 10Gi
  resources:
    requests:
      cpu: 200m
      memory: 1Gi
    limits:
      cpu: 1
      memory: 2Gi
  tls:
    mode: disabled

---
# Property Sharding cluster (Neo4j 2025.12+)
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: sharding-cluster
spec:
  acceptLicenseAgreement: "eval"
  image:
    repo: neo4j
    tag: 2025.12-enterprise
  topology:
    servers: 7  # Sufficient for property sharding workloads
  storage:
    className: fast-ssd
    size: 100Gi
  resources:
    requests:
      cpu: 2
      memory: 8Gi
    limits:
      cpu: 4
      memory: 16Gi
  propertySharding:
    enabled: true
    config:
      db.tx_log.rotation.retention_policy: "14 days"
      internal.dbms.sharded_property_database.property_pull_interval: "5ms"
      server.memory.heap.max_size: "12G"
      server.memory.pagecache.size: "6G"
  tls:
    mode: cert-manager
    issuerRef:
      name: ca-cluster-issuer
      kind: ClusterIssuer

---
# Production cluster (high availability)
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jEnterpriseCluster
metadata:
  name: prod-cluster
spec:
  acceptLicenseAgreement: "eval"
  topology:
    servers: 5
    placement:
      topologySpread:
        enabled: true
        topologyKey: topology.kubernetes.io/zone
        maxSkew: 1
      antiAffinity:
        enabled: true
        type: required
    availabilityZones: [us-east-1a, us-east-1b, us-east-1c]
    enforceDistribution: true
  storage:
    className: fast-ssd
    size: 100Gi
  resources:
    requests:
      cpu: 2
      memory: 8Gi
    limits:
      cpu: 4
      memory: 16Gi
  tls:
    mode: cert-manager
    issuerRef:
      name: ca-cluster-issuer
      kind: ClusterIssuer
  auth:
    adminSecret: neo4j-admin-secret

Best Practices

  1. Resource Planning: Allocate sufficient memory (≥4Gi) and CPU for Neo4j Enterprise workloads
  2. High Availability: Use odd number of servers (3, 5) and distribute across availability zones
  3. Server Role Optimization: Use role constraints to optimize server usage for specific workload patterns
  4. Storage: Use fast SSD storage classes (fast-ssd, premium-ssd) for production workloads
  5. Security: Always enable TLS and use strong password policies in production
  6. Monitoring: Enable query monitoring and centralized logging for performance insights
  7. Backup Strategy: Define a Neo4jBackup CR per backup target (one-shot or scheduled via spec.schedule) with appropriate retention policies
  8. Scaling: Plan for growth - scaling up is easier than scaling down

Troubleshooting

Common Issues

# Check cluster formation
kubectl exec my-cluster-server-0 -- cypher-shell -u neo4j -p password "SHOW SERVERS"

# Check pod status
kubectl get pods -l neo4j.com/cluster=my-cluster
kubectl describe pod my-cluster-server-0

# Check operator logs
kubectl logs -n neo4j-operator-system -l control-plane=controller-manager --tail=-1

# Verify split-brain detection
kubectl get events --field-selector reason=SplitBrainDetected

Resource Conflicts

# Force reconciliation with a no-op annotation change
kubectl annotate neo4jenterprisecluster my-cluster \
  troubleshooting.neo4j.com/reconcile="$(date +%s)" --overwrite

For more information: