Skip to content

Neo4jBackup

This document provides a reference for the Neo4jBackup Custom Resource Definition (CRD). This resource is used for creating and managing backups of Neo4j databases running under either Neo4jEnterpriseCluster or Neo4jEnterpriseStandalone.

For a comprehensive guide on using backups, see the Backup and Restore Guide.

API Version

  • Group: neo4j.neo4j.com
  • Version: v1beta1
  • Kind: Neo4jBackup

How it works

The operator creates a Kubernetes Job that runs neo4j-admin database backup inside a container using the same Neo4j enterprise image as the target cluster. No separate backup image is needed or configured.

Key implementation details:

  • The operator automatically sets server.backup.listen_address=0.0.0.0:6362 in neo4j.conf on the target StatefulSet.
  • The --from flag is automatically populated with the FQDNs of all server pods at port 6362.
  • For cloud storage, --to-path uses native cloud URIs: s3://, gs://, azb://.
  • For PVC storage, --to-path uses the local path within the mounted PVC.
  • RBAC: Only a neo4j-backup-sa ServiceAccount is created. No Role or RoleBinding is created because the backup Job requires no Kubernetes API access.
  • Retention: cloud storage (S3/GCS/Azure) is pruned by your bucket's lifecycle rules, not the operator. For PVC storage, the operator runs a cleanup Job only when the Neo4jBackup CR is deleted — see RetentionPolicy.
  • Scope: set spec.instanceRef (the deployment — a cluster or a standalone) plus exactly one scope field: spec.database (a single database), spec.shardedDatabase (the name of a Neo4jShardedDatabase CR), or spec.allDatabases: true (every user database; the system database is excluded). The operator resolves cluster-vs-standalone itself — topology is not part of the API. spec.allDatabases produces one .backup artifact per database (recorded in status.history[].databaseArtifacts) and is restorable cluster-wide via Neo4jRestore.spec.allDatabases.

Spec

The Neo4jBackupSpec defines the desired state of a Neo4j backup configuration.

Field Type Required Description
instanceRef string ✅ The Neo4j deployment to back up — a Neo4jEnterpriseCluster or Neo4jEnterpriseStandalone. Topology-agnostic. Pair with exactly one of database / shardedDatabase / allDatabases.
database string ❌ Single-database scope: back up exactly this database. Must match ^[a-zA-Z][a-zA-Z0-9.\-]*$ (max 65 characters). Mutually exclusive with shardedDatabase / allDatabases.
shardedDatabase string ❌ Sharded-database scope: the name of the Neo4jShardedDatabase CR (same namespace; ^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$, max 253), not the logical database name — the operator reads the logical name from that CR's spec.name, and the two often differ. Backs up all its shards (<name>-g000, <name>-p000, …) in one neo4j-admin invocation. Mutually exclusive with database / allDatabases.
allDatabases bool ❌ Instance-wide scope: back up every user database (the system database is excluded). Mutually exclusive with database / shardedDatabase.
storage StorageLocation ✅ Where to store the backup. Cloud provider configuration (including workload identity) lives under storage.cloud.
schedule string ❌ Cron expression for automated backups (e.g., "0 2 * * *"). Plain 5-field UTC syntax only — TZ=/CRON_TZ= prefixes are rejected (Kubernetes refuses timezone-embedded CronJob schedules). Scheduled backup names are limited to 40 characters (the generated <name>-backup-cron CronJob must fit Kubernetes' 52-char CronJob-name limit). Removing schedule from an existing CR deletes the CronJob (the CR becomes a one-shot backup).
mode string ❌ standard (default) or replication-source. replication-source marks this backup as the differential chain a cross-cluster replica pulls from, and enforces the constraints that keep that chain usable: single-database scope, no operator-side retention, no competing Neo4jBackup writing to the same storage location, and a required schedule. It also publishes status.replicationPullURI. Limit: for cloud storage the operator never prunes — retention is delegated to bucket lifecycle rules it cannot read — so a lifecycle rule expiring objects in this directory will silently break the chain and force a replica rebuild. Exclude the path from lifecycle expiry. See the Cross-Cluster Replication guide.
retention *RetentionPolicy ❌ Backup retention policy
options *BackupOptions ❌ Backup-specific options
suspend bool ❌ Suspend backups without deleting the resource. For scheduled backups, the operator propagates this to CronJob.spec.suspend, so Kubernetes stops firing scheduled Jobs; setting it back to false resumes the schedule. suspend: true also pauses one-shot backups that haven't run yet.
chainFromBackup string ❌ Names another Neo4jBackup CR in the same namespace whose <base>/<cr-name>/ directory this CR should write into instead of its own. Used to compose mixed-cadence workflows (e.g. a daily FULL CR plus an hourly DIFF CR that chains off the daily's artifacts). Must be a DNS-1123 name (validator-enforced). See Chaining mixed-cadence backups.

Type Definitions

Scope fields

There is no separate target type. Set spec.instanceRef to the deployment (a Neo4jEnterpriseCluster or Neo4jEnterpriseStandalone — the operator auto-detects which) and pick exactly one scope field:

Scope field Type Meaning
database string Back up a single database (e.g. "neo4j", "mydb").
shardedDatabase string The name of a Neo4jShardedDatabase CR; backs up all shards of that logical property-sharded database (e.g. a CR whose spec.name is products → products-g000, products-p000, …) in one neo4j-admin invocation via a glob.
allDatabases bool Back up every user database on the instance (neo4j-admin is invoked with the "*" glob, producing one artifact per database; the system database is excluded).

The scope fields are mutually exclusive — set exactly one.

Example:

# Back up every database on an instance
spec:
  instanceRef: production-cluster
  allDatabases: true

# Back up a single database
spec:
  instanceRef: production-cluster
  database: mydb

StorageLocation

Defines where to store backups.

Field Type Required Description
type string ✅ Storage type: "s3", "gcs", "azure", "pvc"
bucket string ❌ Bucket or container name (required for cloud storage types). Restricted to A-Z a-z 0-9 . _ / - — the validator rejects other characters (the value is interpolated into the Job's shell command).
path string ❌ Path within the bucket or PVC. Same A-Z a-z 0-9 . _ / - charset restriction as bucket. Defaults to backups for cloud storage when empty.
pvc *PVCSpec ❌ PVC configuration (required when type=pvc, with pvc.name set)
cloud *CloudBlock ❌ Cloud provider configuration including optional credentials secret. Required for s3 / gcs / azure, with cloud.provider set to aws / gcp / azure respectively

CloudBlock

Cloud provider configuration. This type lives on StorageLocation as storage.cloud — it carries both per-storage credentials and workload-identity setup.

Field Type Required Description
provider string ❌ Cloud provider: "aws", "gcp", "azure". Required when storage.type is s3 (aws), gcs (gcp) or azure (azure) — the validator rejects a missing or mismatched provider
credentialsSecretRef string ❌ Name of a Kubernetes Secret containing cloud provider credentials as environment variables. When absent, ambient workload identity (IRSA / GKE WI / Azure WI) is used instead.
identity *CloudIdentity ❌ Cloud identity configuration (for workload identity ServiceAccount annotations)
endpointURL string ❌ Override the S3 API endpoint. Use for S3-compatible stores such as MinIO, Ceph RGW, or Cloudflare R2 (e.g. "http://minio.minio.svc:9000"). Only applies when provider: aws.
forcePathStyle bool ❌ Force S3 path-style addressing (endpoint/bucket/key instead of bucket.endpoint/key). Required for MinIO and most self-hosted S3-compatible stores. Only effective when endpointURL is set.

Secret key requirements by provider (when credentialsSecretRef is set):

Provider Required secret keys Notes
AWS AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION Standard AWS SDK env vars
MinIO / S3-compatible AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION Same keys as AWS; set endpointURL and forcePathStyle: true on CloudBlock
GCS GOOGLE_APPLICATION_CREDENTIALS_JSON Full service-account key JSON as a string value — not a filename path
Azure AZURE_STORAGE_ACCOUNT, AZURE_STORAGE_KEY Storage account credentials

Example — creating cloud credential secrets:

# AWS
kubectl create secret generic aws-backup-credentials \
  --from-literal=AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE \
  --from-literal=AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \
  --from-literal=AWS_REGION=us-east-1

# MinIO (uses the same keys; region value is arbitrary — MinIO ignores it)
kubectl create secret generic minio-backup-credentials \
  --from-literal=AWS_ACCESS_KEY_ID=minioadmin \
  --from-literal=AWS_SECRET_ACCESS_KEY=minioadmin \
  --from-literal=AWS_REGION=us-east-1

# GCS — pass the JSON content directly as a string value
kubectl create secret generic gcs-backup-credentials \
  --from-literal=GOOGLE_APPLICATION_CREDENTIALS_JSON="$(cat service-account.json)"

# Azure
kubectl create secret generic azure-backup-credentials \
  --from-literal=AZURE_STORAGE_ACCOUNT=myaccount \
  --from-literal=AZURE_STORAGE_KEY=base64key==

MinIO / S3-compatible example:

storage:
  type: s3
  bucket: neo4j-backups
  path: cluster/full
  cloud:
    provider: aws
    credentialsSecretRef: minio-backup-credentials
    endpointURL: http://minio.minio.svc:9000   # in-cluster MinIO service
    forcePathStyle: true                        # required for MinIO

How it works: endpointURL is injected as AWS_ENDPOINT_URL_S3 (AWS SDK v2 standard). forcePathStyle: true injects -Daws.s3.forcePathStyle=true via JAVA_TOOL_OPTIONS, which the neo4j-admin JVM process reads at startup.

CloudIdentity

Cloud identity configuration for workload identity scenarios (no static credentials).

Field Type Required Description
provider string ✅ Identity provider: "aws", "gcp", "azure"
serviceAccount string ❌ RESERVED — currently a no-op. Backup Jobs always run as the operator-managed neo4j-backup-sa (restore Jobs: neo4j-restore-sa); this field is accepted for backward compatibility but not read. Bind your cloud IAM role via autoCreate.annotations instead. A ValidationWarning event is raised when it is set.
autoCreate *AutoCreateSpec ❌ Workload-identity annotations for the operator-managed ServiceAccount

AutoCreateSpec

Carries workload-identity annotations for the operator-managed ServiceAccount.

Field Type Required Description
enabled bool ❌ RESERVED — currently a no-op. The operator always ensures the backup/restore ServiceAccount exists; only annotations is honored. A ValidationWarning event is raised when it is set to false (the schema default is true, so only false is detectable).
annotations map[string]string ❌ Annotations applied to the neo4j-backup-sa ServiceAccount on every reconcile. Use this to attach workload-identity annotations.

The annotations in autoCreate.annotations are applied to the neo4j-backup-sa ServiceAccount on every reconcile, so they stay in sync with the desired state.

Workload identity annotation examples:

# AWS IRSA
autoCreate:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/neo4j-backup-role

# GKE Workload Identity
autoCreate:
  annotations:
    iam.gke.io/gcp-service-account: neo4j-backup@my-project.iam.gserviceaccount.com

# Azure Workload Identity
autoCreate:
  annotations:
    azure.workload.identity/client-id: 00000000-0000-0000-0000-000000000000

PVCSpec

PVC configuration for local storage.

Field Type Required Description
storageClassName string ❌ Storage class name for dynamic provisioning (used only when the operator creates the PVC, i.e. size is set and the PVC does not exist)
name string ✅ (for type: pvc) Name of the PVC to use. Always required: the validator rejects a PVC backup without it. If the PVC does not exist and size is set, the operator creates it under this name (without an owner reference, so it survives deletion of the CR)
size string ❌ Size for a new PVC (e.g., "100Gi"); omit to reference an externally provisioned PVC. Must be a valid Kubernetes quantity greater than zero — validation refuses anything else, and the controller reports phase Invalid (naming this field)

RetentionPolicy

Backup retention configuration.

Field Type Required Description
maxAge string ❌ Maximum age of artifacts to retain. A single unit of d (days), h (hours), m (minutes), or s (seconds) — e.g. "30d", "168h", "90m". Compound values ("1h30m") and "4w" are rejected by the validator (the runtime applies exactly what validates).
maxCount int32 ❌ Maximum number of .backup artifacts to retain
deletePolicy string ❌ "Delete" (default, and the only accepted value). Expired PVC-stored artifacts are pruned by the delete-time cleanup Job; for cloud storage, retention is delegated to bucket lifecycle rules.

When retention is set it must contain maxAge and/or maxCount — an empty retention: {} is rejected (phase: Invalid) — and maxCount may not be negative.

How retention actually works (read this before relying on it):

  • Cloud storage (S3/GCS/Azure): the operator never deletes cloud objects. Configure bucket lifecycle rules on the provider side; retention on a cloud-backed CR is advisory only.
  • PVC storage: retention is enforced by a cleanup Job that runs only when the Neo4jBackup CR is deleted — it is not a continuous pruning loop. The Job prunes *.backup files inside this CR's chain directory (/backup/<chain-root>/) only, oldest-first by file mtime, and always keeps the newest artifact even if it has aged out. A split artifact's data parts (<name>.backup.1, .2, …) are deleted with it, and count as part of one artifact. Other CRs' chain directories on a shared PVC are never touched.
  • DIFF-orphaning caveat: artifact filenames don't encode FULL vs DIFF, so pruning can orphan differential artifacts whose parent FULL ages out (making them unrestorable). The operator emits a BackupRetentionCaveat Warning event when retention is configured on a CR that can produce DIFFs. Prefer options.backupType: FULL on CRs that use retention, or prune chains with neo4j-admin backup aggregate.

BackupOptions

Fine-grained backup execution options.

Field Type Required Description
resources *corev1.ResourceRequirements ❌ CPU/memory requests + limits on the backup Job's container. When unset, the operator applies a Burstable default (request 100m CPU / 512Mi memory, limit 1 CPU / 2Gi memory) sized for small databases and CI. Tune upward for large production databases.
compress *bool ❌ Compress the backup (default: true).
backupType string ❌ Backup type: "FULL", "DIFF", "AUTO" (default). AUTO takes a full backup the first time and differentials after — except for a sharded database (spec.shardedDatabase) on a PVC, where every run is full, because only full shard backups can seed a sharded database from a PVC
preferDiffAsParent bool ❌ Use the latest differential backup as the parent when creating a new differential backup (default: false). Maps to --prefer-diff-as-parent. Requires CalVer 2025.04+ — an error is returned at runtime if the target version does not support this flag.
tempPath string ❌ Local directory path for temporary files during backup. When tempStorage is configured, this is set automatically. Only set manually if you are mounting your own volume. Maps to --temp-path. Must be an absolute path restricted to A-Z a-z 0-9 . _ / - (validator-enforced). The directory must already exist in the container — for staging, prefer tempStorage, which mounts a volume at the path.
tempStorage *TempStorageSpec ❌ Provisions a PVC for temporary staging files during cloud backups. The operator mounts this PVC and passes --temp-path automatically. Recommended for large databases to avoid filling ephemeral disk.
pageCache string ❌ Page cache size hint (e.g., "4G"). Must match pattern ^[0-9]+[KMG]?$
validate *bool ❌ When true, runs neo4j-admin backup validate against the artifacts after the backup succeeds, recording per-shard recoverability into status.history[].validation. Appended with \|\| true so validate failures don't fail the Job (the backup already succeeded). Unset (default) skips validate. Requires a CalVer (2025.x+) Neo4j image — on 5.26 the neo4j-admin backup validate subcommand does not exist, so the option has no effect and validation stays empty.
parallelDownload bool ❌ Enable parallel download for remote backups
remoteAddressResolution *bool ❌ Resolve remote addresses via the cluster discovery service (useful in multi-homed environments). When unset and the backup is shardedDatabase-scoped on Neo4j 2025.09+, the operator defaults this to true to match the canonical upstream sharded-backup invocation; otherwise unset. Set explicitly (true or false) to override in either direction.
skipRecovery bool ❌ Skip the recovery step after backup
includeMetadata string ❌ Controls which metadata is included in the backup. Values: "all" (default), "none", "users", "roles". Requires Neo4j 5.26+.
parallelRecovery bool ❌ Enable multi-threaded transaction application during backup
keepFailed bool ❌ Preserve failed backup artifacts for debugging instead of deleting them
splitArchivePartSize string ❌ Write each artifact as several files of at most this size, for an artifact larger than the target storage accepts as one object. A Kubernetes quantity of at least 1Gi, Neo4j's minimum part size (e.g. "500Gi"); unset or "0" writes a single file. Maps to --split-archive-part-size, passed as an exact byte count — note 1G is 10⁹ bytes, below the minimum, and is refused (phase: Invalid). Requires Neo4j 2026.09+ on the target; on an older image the backup is refused with a message naming the field. Cannot also be set in additionalArgs. See Split backup archives.
additionalArgs []string ❌ Additional arguments passed verbatim to neo4j-admin database backup. Each must start with -; the removed --cc-graph, --cc-indexes, --cc-label-scan-store and --legacy-format are rejected by the validator

preferDiffAsParent version requirement: This flag was introduced in Neo4j CalVer 2025.04. Using it against Neo4j 5.26.x or CalVer 2025.01–2025.03 will cause the backup Job to fail with an unsupported argument error. The operator validates this at runtime and returns an error before creating the Job.

TempStorageSpec

Provisions temporary staging storage for cloud backup/restore. The operator creates a PVC, mounts it at /tmp/neo4j-staging in the Job pod, and passes --temp-path=/tmp/neo4j-staging to neo4j-admin. The PVC (named <cr-name>-temp-staging) is owned by the Neo4jBackup (or Neo4jRestore) CR, not by the Job: it outlives the Job's TTL and is garbage-collected only when that CR is deleted.

Field Type Required Description
size string ✅ Size of the temporary PVC (e.g., "50Gi"). Should be at least as large as the expected backup artifact. Must match pattern ^[0-9]+(Ki\|Mi\|Gi\|Ti)?$
storageClassName string ❌ StorageClass for the temporary PVC. If empty, uses the cluster default.

Status

The Neo4jBackupStatus represents the observed state of the backup.

Field Type Description
conditions []metav1.Condition Current backup conditions. The operator maintains a single Ready condition derived from phase (Completed → True with reason BackupSucceeded; Scheduled → True with reason BackupScheduled; Suspended → False with reason Suspended — a deliberate pause, not a failure; Failed/Invalid → False with reason ReconciliationFailed; everything else → Unknown).
phase string Current backup phase — see Backup Phases
message string Human-readable message about the current state
observedGeneration int64 The .metadata.generation most recently observed by the controller
lastRunTime *metav1.Time When the last backup Job started
lastSuccessTime *metav1.Time When a one-shot backup completed (set on the Completed phase transition). Never set for scheduled backups — they don't pass through Completed; read status.history[] instead.
nextRunTime *metav1.Time Reserved — not currently populated by the operator. For scheduled backups the CronJob's own schedule is authoritative (kubectl get cronjob <name>-backup-cron); read status.history[] for actual run times.
stats *BackupStats Statistics from the most recent backup run (only duration is populated — see BackupStats).
history []BackupRun History of recent backup runs. For scheduled backups this is the authoritative record of run outcomes.
replicationPullURI string Populated only when spec.mode is replication-source and the storage is cloud (s3://, gs:// or azb://; empty for pvc). The exact object-storage directory a downstream cross-cluster replica pulls this chain from — paste it into Neo4jReplicaDatabase.spec.source.pullURI instead of assembling bucket + path + chain directory by hand.

Backup Phases

Phase Meaning
Pending A transient precondition isn't met yet — e.g. the chainFromBackup parent CR doesn't exist yet, another Job in the same chain is still running, or the sharded-backup preflight couldn't connect to the cluster. The controller requeues and retries.
Waiting The target cluster/standalone CR doesn't exist yet (common with kubectl apply -f dir/ ordering) or isn't accepting work yet (a cluster still forming or without a majority; a standalone not Ready). A Degraded cluster is backed up. Transient — the controller requeues.
Scheduled A CronJob has been created for spec.schedule. This is the steady state for scheduled backups — they never transition to Completed; per-run outcomes accumulate in status.history[]. The Ready condition is True (reason BackupScheduled) in this phase.
Suspended spec.suspend: true — the CronJob is suspended and one-shot runs are paused. The Ready condition is False with reason Suspended (a deliberate pause, not a failure).
Running A one-shot backup Job is executing.
Completed The one-shot backup Job succeeded. Terminal — re-running requires deleting and recreating the CR.
Failed The one-shot backup Job failed terminally (Job Failed condition, after retries) or a non-transient error occurred (e.g. chain target/storage mismatch). Terminal for one-shot backups.
Invalid The spec failed validation (clear aggregated message in status.message). Recoverable — fixing the spec re-triggers reconcile.

Ready=True on a scheduled backup means the CronJob is armed, not that a run succeeded. A scheduled backup stays in Scheduled (Ready=True, reason BackupScheduled) for its whole life, so kubectl wait --for=condition=Ready returns as soon as the CronJob exists — long before the first run. Poll status.history[*].status for Succeeded to see whether a run completed.

BackupStats

Field Type Description
size string Reserved — accepted by the schema but never populated today. Total backup size (e.g., "2.5GB")
duration string Backup operation duration (e.g., "5m30s")
throughput string Reserved — accepted by the schema but never populated today. Backup throughput rate (e.g., "8.3MB/s")
fileCount int32 Reserved — accepted by the schema but never populated today. Number of files in the backup

Only duration is currently populated. size, throughput, and fileCount are reserved and not yet emitted by the operator — treat them as a forward-looking schema. To inspect artifact sizes, look at the .backup files under status.history[*].backupsPath in your storage backend.

BackupRun

Represents a single backup Job execution.

Field Type Description
runID string Unique identifier for this run, populated from the backing Job's metadata.name (not its opaque UID) — e.g. <backup>-backup for one-shots, <backup>-backup-cron-<unix-seconds> for CronJob children. Used by the operator to dedupe history entries across reconciles and by users to correlate a history entry with the actual Job (kubectl logs job/<runID>).
startTime metav1.Time When the backup run started
completionTime *metav1.Time When the run completed (nil if still running)
status string Run status: "Running", "Succeeded", "Failed"
error string Error message if the backup failed
stats *BackupStats Backup statistics for this run
backupsPath string The shared per-CR directory under spec.storage.path where this run wrote its .backup artifact. Same value for every run of one CR — all runs accumulate in this directory so neo4j-admin can chain differential backups off the prior full. Value is the Neo4jBackup CR name (or the chainFromBackup chain-root name when set). Use the runID field (Job name) for per-run identity.
artifactFilename string Filename of the .backup artifact produced by this standard-DB run (e.g. "neo4j-2026-06-08T01-18-06.backup"). Populated by parsing the Job's Pod log after completion; empty when logs couldn't be fetched or the pattern didn't match. Used by the online PVC-restore path to build a per-restore seed URL.
artifactType string FULL or DIFF: whether artifactFilename is a full backup or a differential that needs the rest of its chain. Read from the Job's Pod log (AUTO that fell back to a full is FULL); empty when the log did not say. An online restore from a PVC seeds one file over HTTP, so for anything but FULL the seed proxy merges the chain into one full artifact first.
shardArtifacts []ShardArtifact Per-shard .backup files produced by a sharded backup run (spec.shardedDatabase). Empty for non-sharded runs.
databaseArtifacts []DatabaseArtifact Per-database .backup files produced by an all-databases backup (spec.allDatabases): one entry (database, filename, size, type — FULL/DIFF as for artifactType) per user database (shard physical databases excluded). Consumed by an all-databases restore (Neo4jRestore.spec.allDatabases). Empty for single-database and sharded runs.
shardedDatabasesExcluded []string Logical property-sharded databases (e.g. products) captured by this all-databases run that the all-databases restore loop does not recreate (they need shard-topology CREATE clauses only Neo4jShardedDatabase emits). They are not lost — their per-shard files are catalogued in shardedFamilies, so each is restorable from this backup via its Neo4jShardedDatabase CR (spec.seedBackupRef). Also raised as a BackupShardedDatabasesExcluded event. Empty when the run had no sharded families.
shardedFamilies []ShardedFamilyArtifacts Per-family shard .backup artifacts catalogued by an all-databases backup (one entry per logical family, each with its shardArtifacts). Makes one all-databases backup in cloud storage a complete DR source: a Neo4jShardedDatabase whose spec.seedBackupRef points here is seeded from these. On a PVC only a run whose shards are all FULL seeds — back sharded databases up there with a shardedDatabase-scoped backup. Empty for single-database and single-family shardedDatabase-scoped runs (those use shardArtifacts).
validation *BackupValidationResult Per-shard outcome of the optional neo4j-admin backup validate step (only when options.validate=true and the operator could parse the output).

ShardArtifact

Identifies one .backup file produced by a sharded backup.

Field Type Description
shardName string Per-shard database name (e.g. "products-g000", "products-p000"). Derived by stripping the timestamp suffix from the neo4j-admin output filename.
filename string On-disk filename as written by neo4j-admin (e.g. "products-g000-2025-06-11T21-04-42.backup").
size int64 Artifact size in bytes. Zero if not parseable.
type string FULL or DIFF, read from the Job's Pod log; empty when the log did not say. A sharded database seeds from a PVC only when every shard of the run is FULL — see Restoring a sharded database.

ShardedFamilyArtifacts

One property-sharded family's per-shard artifacts as catalogued by an all-databases backup, so each family is restorable from that single backup via its Neo4jShardedDatabase CR (spec.seedBackupRef).

Field Type Description
family string Logical sharded-database name (e.g. "products") — Neo4jShardedDatabase.spec.name, the prefix shared by every shard.
shardArtifacts []ShardArtifact Per-shard .backup files for this family (graph shard {family}-g000 + property shards {family}-pNNN).

BackupValidationResult

Captures the output of neo4j-admin backup validate run after a backup, surfacing per-shard recoverability.

Field Type Description
overallStatus string "OK" if every shard's chain is recoverable to the same transaction or higher; "Degraded" if any shard is ahead/behind beyond the lenient consistency window; "Unknown" if validate failed or its output couldn't be parsed.
perShard []ShardValidationStatus The validate command's status report for each shard.
rawOutput string Truncated stdout of neo4j-admin backup validate, kept for debugging when the parser couldn't classify a per-shard line. Capped at 2 KiB.

ShardValidationStatus

One row of validate output.

Field Type Description
shardName string Per-shard database name.
status string One of "OK", "Ahead", "Behind", "Unknown".
message string Human-readable detail for this shard's status.

Chaining mixed-cadence backups

spec.chainFromBackup lets a higher-frequency differential backup share artifact storage with a lower-frequency full backup, so neo4j-admin database backup --type=DIFF can auto-detect and chain off the prior full/diff in the shared directory.

A typical setup is a daily FULL backup CR plus an hourly DIFF backup CR that points chainFromBackup at the daily CR. Both write into <base>/<daily-cr-name>/.

Constraints (enforced by the validator):

  • Must reference a CR in the same namespace.
  • Cannot self-reference (chainFromBackup: <this-cr-name> is rejected).
  • Scope (instanceRef + database/sharded/all-databases scope) must match the referenced CR.
  • Storage backend (type + bucket/path) must match.

Runtime safety: the operator labels every backup Job with app.kubernetes.io/part-of: <chain-root-name> and refuses to submit a new Job while another Job sharing the same part-of label is still active — preventing the daily FULL and hourly DIFF from racing against the same artifact directory.

When empty (default), the CR writes to its own per-name directory.

# Daily FULL backup (the chain root)
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: daily-full
  namespace: neo4j
spec:
  instanceRef: production-cluster
  allDatabases: true
  storage:
    type: s3
    bucket: neo4j-backups
    path: prod/
    cloud:
      provider: aws
  schedule: "0 2 * * *"
  options:
    backupType: FULL
---
# Hourly DIFF backup chaining off the daily full
apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: hourly-diff
  namespace: neo4j
spec:
  instanceRef: production-cluster
  allDatabases: true
  storage:
    type: s3
    bucket: neo4j-backups
    path: prod/
    cloud:
      provider: aws
  schedule: "0 * * * *"
  chainFromBackup: daily-full   # share daily-full's artifact directory
  options:
    backupType: DIFF

Examples

Scheduled S3 Backup (Cluster) with IRSA

Uses AWS IRSA workload identity — no static credentials needed.

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: daily-cluster-backup
  namespace: neo4j
spec:
  instanceRef: production-cluster
  allDatabases: true
  storage:
    type: s3
    bucket: neo4j-backups
    path: daily/
    cloud:
      provider: aws
      identity:
        provider: aws
        autoCreate:
          annotations:
            eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/neo4j-backup-role
  schedule: "0 2 * * *"   # Daily at 2 AM UTC
  retention:
    maxAge: "30d"
    maxCount: 30
  options:
    compress: true
    backupType: FULL
    tempStorage:
      size: "50Gi"

Scheduled S3 Backup with Static Credentials

Uses an explicit Kubernetes Secret for AWS credentials instead of IRSA.

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: daily-cluster-backup-static-creds
  namespace: neo4j
spec:
  instanceRef: production-cluster
  allDatabases: true
  storage:
    type: s3
    bucket: neo4j-backups
    path: daily/
    cloud:
      provider: aws
      credentialsSecretRef: aws-backup-credentials   # Secret with AWS_ACCESS_KEY_ID etc.
  schedule: "0 2 * * *"
  retention:
    maxAge: "30d"
    maxCount: 30
  options:
    compress: true
    backupType: FULL
    tempStorage:
      size: "50Gi"

Single-Database Backup to S3

Backs up only one database. Set database (the database name) alongside instanceRef (the cluster or standalone that owns it).

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: mydb-daily-backup
  namespace: neo4j
spec:
  instanceRef: production-cluster   # The cluster or standalone that hosts the database
  database: mydb                     # The Neo4j database name
  storage:
    type: s3
    bucket: neo4j-backups
    path: mydb/daily/
    cloud:
      provider: aws
      credentialsSecretRef: aws-backup-credentials
  schedule: "0 3 * * *"
  options:
    compress: true
    backupType: AUTO
    tempStorage:
      size: "50Gi"

Differential Backup with preferDiffAsParent (CalVer 2025.04+)

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: hourly-diff-backup
  namespace: neo4j
spec:
  instanceRef: production-cluster-2025
  allDatabases: true
  storage:
    type: s3
    bucket: neo4j-backups
    path: hourly-diff/
    cloud:
      provider: aws
      credentialsSecretRef: aws-backup-credentials
  schedule: "0 * * * *"   # Every hour
  options:
    backupType: DIFF
    preferDiffAsParent: true   # Requires Neo4j CalVer 2025.04+
    tempStorage:
      size: "50Gi"
    compress: true

On-Demand PVC Backup

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: manual-pvc-backup
  namespace: neo4j
spec:
  instanceRef: staging-cluster
  database: mydb
  storage:
    type: pvc
    pvc:
      name: backup-storage
    path: backups/manual/
  options:
    compress: true
    validate: true   # runs `neo4j-admin backup validate` after the backup
    backupType: DIFF

GCS Backup with GKE Workload Identity

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: weekly-gcs-backup
  namespace: neo4j
spec:
  instanceRef: analytics-cluster
  allDatabases: true
  storage:
    type: gcs
    bucket: neo4j-analytics-backups
    path: weekly/
    cloud:
      provider: gcp
      identity:
        provider: gcp
        autoCreate:
          annotations:
            iam.gke.io/gcp-service-account: neo4j-backup@my-project.iam.gserviceaccount.com
  schedule: "0 3 * * 0"   # Weekly on Sunday at 3 AM
  retention:
    maxCount: 12
  options:
    backupType: AUTO
    pageCache: "8G"
    tempStorage:
      size: "50Gi"

GCS Backup with Static Service Account Credentials

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: weekly-gcs-backup-static
  namespace: neo4j
spec:
  instanceRef: analytics-cluster
  allDatabases: true
  storage:
    type: gcs
    bucket: neo4j-analytics-backups
    path: weekly/
    cloud:
      provider: gcp
      credentialsSecretRef: gcs-backup-credentials   # Secret with GOOGLE_APPLICATION_CREDENTIALS_JSON
  schedule: "0 3 * * 0"
  retention:
    maxCount: 12
  options:
    backupType: AUTO
    pageCache: "8G"
    tempStorage:
      size: "50Gi"

Azure Backup with Azure Workload Identity

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: daily-azure-backup
  namespace: neo4j
spec:
  instanceRef: enterprise-cluster
  allDatabases: true
  storage:
    type: azure
    bucket: neo4j-backups         # Azure storage container name
    path: daily/
    cloud:
      provider: azure
      identity:
        provider: azure
        autoCreate:
          annotations:
            azure.workload.identity/client-id: 00000000-0000-0000-0000-000000000000
  schedule: "0 1 * * *"
  retention:
    maxAge: "14d"
  options:
    compress: true
    backupType: FULL
    tempStorage:
      size: "50Gi"

Azure Backup with Static Credentials

apiVersion: neo4j.neo4j.com/v1beta1
kind: Neo4jBackup
metadata:
  name: daily-azure-backup-static
  namespace: neo4j
spec:
  instanceRef: enterprise-cluster
  allDatabases: true
  storage:
    type: azure
    bucket: neo4j-backups
    path: daily/
    cloud:
      provider: azure
      credentialsSecretRef: azure-backup-credentials   # Secret with AZURE_STORAGE_ACCOUNT and AZURE_STORAGE_KEY
  schedule: "0 1 * * *"
  retention:
    maxAge: "14d"
  options:
    compress: true
    backupType: FULL
    tempStorage:
      size: "50Gi"

Monitoring

# List all backup resources
kubectl get neo4jbackup -n neo4j

# View backup status and last run time
kubectl get neo4jbackup daily-cluster-backup -o wide

# Describe a backup for detailed status and events
kubectl describe neo4jbackup daily-cluster-backup

# Watch backup status changes
kubectl get neo4jbackup daily-cluster-backup -w

# Check logs from the most recent backup Job
kubectl logs -n neo4j -l neo4j.com/backup=daily-cluster-backup --tail=100

# Check backup phase (scheduled backups stay in "Scheduled"; only one-shot
# backups reach "Completed")
kubectl get neo4jbackup daily-cluster-backup -o jsonpath='{.status.phase}'

# Check run outcomes — for scheduled backups, status.history is the record
# (lastSuccessTime is only set for one-shot backups)
kubectl get neo4jbackup daily-cluster-backup -o jsonpath='{.status.history[*].status}'

For more information on backup operations, see the Backup and Restore Guide.