KafkaBackupValidation CRD
The KafkaBackupValidation custom resource validates backups and can produce evidence reports for compliance workflows.
Example
apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaBackupValidation
metadata:
name: daily-backup-validation
namespace: kafka-backup
spec:
backupRef:
name: production-backup
backupId: "production-backup-20260413-120000"
kafkaCluster:
bootstrapServers:
- kafka:9092
connection:
connectionsPerBroker: 4
checks:
messageCount:
enabled: true
failThreshold: 0
topics:
- orders
offsetRange:
enabled: true
consumerGroupOffsets:
enabled: true
consumerGroups:
- order-processor
evidence:
formats:
- json
- pdf
retentionDays: 90
signing:
enabled: true
keySecret:
name: evidence-signing-key
privateKeyKey: signing-key.pem
publicKeyKey: signing-key-pub.pem
schedule: "0 0 2 * * * *"
suspend: false
Spec Fields
backupRef
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | KafkaBackup resource to validate; use "" when using direct storage |
namespace | string | No | Backup namespace; defaults to the validation namespace |
backupId | string | No | Specific backup ID to validate |
storage | object | No | Direct storage reference for external backups |
checks
| Field | Type | Description |
|---|---|---|
messageCount | object | Compare record counts against the backup manifest |
offsetRange | object | Verify backup offset ranges |
consumerGroupOffsets | object | Verify consumer group positions are backed up |
customWebhooks | array | POST validation payloads to external endpoints |
kafkaCluster is required: the checks compare the backup against this cluster, and a validation without it fails. The shared kafkaCluster.connection tuning fields are supported here, along with tlsSecret, caSecret, and saslSecret for secure clusters.
evidence
Operator releases before 1.6.2 accepted evidence but never wrote a report: a run reported Completed with no evidence in storage and no path in status (#93).
| Field | Type | Default | Description |
|---|---|---|---|
formats | []string | ["json"] | json and/or pdf (case-insensitive). Any other value, or an empty list, fails validation |
signing | object | unset | ECDSA-P256-SHA256 signing configuration |
storage | object | the backup's storage | Where reports are written, in the same format as KafkaBackup.spec.storage |
retentionDays | int | 90 | Retention recorded in the report's compliance mappings |
Reports are written to evidence-reports/<reportId>/<yyyy>/<mm>/ under evidence.storage (and its prefix), or under the backup's own storage when evidence.storage is unset, the same layout as kafka-backup validation run, so kafka-backup validation evidence-list and evidence-get work against it. Each run writes:
<reportId>.json: the report (always written when signing is enabled, because the signature covers its bytes)<reportId>.pdf: whenpdfis informats<reportId>.sig: the detached signature envelope, when signing is enabled
<reportId> is <name>-<yyyymmdd>-<hhmmss>-<first 8 characters of the resource uid>. The report records the storage location without credentials (for example azure://account/container/prefix) and kafka-backup-operator with its version as the tool.
If the reports cannot be stored, the run fails with condition reason EvidenceEmissionFailed; the check results are not reported as a pass without their evidence.
Signing key. signing.keySecret names a Secret holding a PEM-encoded PKCS#8 ECDSA P-256 private key (-----BEGIN PRIVATE KEY-----) under privateKeyKey (default signing-key.pem). A SEC1 key (-----BEGIN EC PRIVATE KEY-----, as openssl ecparam -genkey writes) fails validation with the conversion command:
openssl pkcs8 -topk8 -nocrypt -in key.pem -out key.p8.pem
See Evidence Signing to create a key pair and verify reports.
notifications
Send each run's result and evidence location to Slack and/or PagerDuty. A delivery failure is reported in the status message and does not fail the run.
notifications:
slack:
webhookSecret:
name: validation-slack
key: webhook-url
pagerduty:
routingKeySecret:
name: validation-pagerduty
key: routing-key
severity: critical # critical, error, warning or info
schedule
schedule uses the same cron parser as KafkaBackup and includes seconds. For example, 0 0 2 * * * * runs daily at 02:00 UTC. A validation runs once when it is created or its spec changes, then at every tick of its schedule, after a passed or failed run alike; status.nextScheduledValidation shows the next tick.
Operator releases before 1.6.3 ran a scheduled validation only once, at creation or spec change, and never again (#105).
suspend: true stops scheduled runs. Setting it does not start a run (before 1.6.3 it did, once).
Status
status:
phase: Completed
validationResult: Pass
checksTotal: 3
checksCompleted: 3
checksPassed: 3
evidenceReportId: daily-backup-validation-20260414-020000-3f2a9c1e
evidenceReportPath: evidence-reports/daily-backup-validation-20260414-020000-3f2a9c1e/2026/04/daily-backup-validation-20260414-020000-3f2a9c1e.json
evidencePdfPath: evidence-reports/daily-backup-validation-20260414-020000-3f2a9c1e/2026/04/daily-backup-validation-20260414-020000-3f2a9c1e.pdf
evidenceSignaturePath: evidence-reports/daily-backup-validation-20260414-020000-3f2a9c1e/2026/04/daily-backup-validation-20260414-020000-3f2a9c1e.sig
evidenceReportSha256: 9f2b6c0d4e1a8b7f3c5d2e9a0b4c6d8e1f3a5b7c9d0e2f4a6b8c0d1e3f5a7b9c
evidenceReportSigned: true
lastValidationTime: "2026-04-14T02:00:00Z"
nextScheduledValidation: "2026-04-15T02:00:00Z"
| Field | Description |
|---|---|
evidenceReportId | Report ID of the last completed run |
evidenceReportPath | Key of the JSON report, relative to the evidence storage and its prefix |
evidencePdfPath | Key of the PDF report |
evidenceSignaturePath | Key of the signature envelope |
evidenceReportSha256 | Lowercase hex SHA-256 of the exact stored JSON bytes |
evidenceReportSigned | Whether the report is signed |
nextScheduledValidation | Next scheduled run (scheduled validations, from 1.6.3) |
The evidence fields describe the last completed run; a field the run did not produce is removed. The evidence* fields other than evidenceReportPath and evidenceReportSigned need the CRDs from 1.6.2 or later (kubectl apply --server-side -f deploy/crds/all.yaml): on an older CRD the API server drops them.
A run that fails sets phase: Failed and a Ready condition with one of these reasons:
| Reason | Meaning |
|---|---|
ValidationFailed | The spec is invalid or the checks could not run |
EvidenceEmissionFailed | The checks ran but the evidence could not be stored (1.6.2+) |
Interrupted | The operator restarted while the validation was running; it is not resumed (1.6.5+). A scheduled validation runs again at its next tick; change the spec to run a one-off validation again |