KafkaRestore CRD
The KafkaRestore custom resource restores a backup into a target Kafka cluster.
Full Specification
apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaRestore
metadata:
name: production-restore
namespace: kafka-backup
spec:
backupRef:
name: production-backup
namespace: kafka-backup
backupId: "production-backup-20260413-120000"
# storage can be set here instead of name/namespace for external backups.
kafkaCluster:
bootstrapServers:
- kafka-0.kafka.svc:9092
- kafka-1.kafka.svc:9092
securityProtocol: SASL_SSL
caSecret: # optional: separate CA secret (e.g. Strimzi)
name: cluster-ca-cert
caKey: ca.crt
tlsSecret:
name: kafka-tls
caKey: ca.crt
saslSecret:
name: kafka-credentials
mechanism: SCRAM-SHA-512
connection:
tcpKeepalive: true
keepaliveTimeSecs: 60
keepaliveIntervalSecs: 20
tcpNodelay: true
connectionsPerBroker: 4
topics:
- orders
- payments
topicMapping:
orders: restored-orders
partitionMapping:
0: 0
repartitioning:
restored-orders:
strategy: murmur2
targetPartitions: 6
pitr:
startTime: "2026-04-13T10:00:00Z"
endTime: "2026-04-13T12:00:00Z"
offsetReset:
enabled: true
consumerGroups:
- order-processor
strategy: manual
rollback:
snapshotBeforeRestore: true
snapshotRetentionHours: 24
autoRollbackOnFailure: false
dryRun: false
produceBatchSize: 1000
produceAcks: -1
produceTimeoutMs: 30000
purgeTopics: false
autoConsumerGroups: false
createTopics: true
defaultReplicationFactor: 3
template:
pod:
hostAliases:
- ip: "10.10.0.5"
hostnames:
- s3.internal
- minio.internal
Spec Fields
backupRef
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | KafkaBackup resource to restore from; use "" when using direct storage |
namespace | string | No | Namespace of the KafkaBackup; defaults to the restore namespace |
backupId | string | No | Specific backup ID |
storage | object | No | Direct storage reference for external backups |
kafkaCluster
| Field | Type | Required | Description |
|---|---|---|---|
bootstrapServers | []string | Yes | Target Kafka broker addresses |
securityProtocol | string | No | PLAINTEXT, SSL, SASL_PLAINTEXT, or SASL_SSL |
tlsSecret | object | No | TLS certificate secret reference |
caSecret | object | No | Separate CA certificate secret (e.g. Strimzi cluster CA). Overrides caKey in tlsSecret when both are set |
saslSecret | object | No | SASL credentials secret reference |
connection | object | No | Kafka TCP connection tuning, including connectionsPerBroker |
pitr
| Field | Type | Required | Description |
|---|---|---|---|
startTimestamp | int | No | Start timestamp in epoch milliseconds |
endTimestamp | int | No | End timestamp in epoch milliseconds |
startTime | string | No | Start timestamp as ISO 8601 |
endTime | string | No | End timestamp as ISO 8601 |
Restore Tuning
| Field | Type | Default | Description |
|---|---|---|---|
topics | []string | [] | Topics to restore; empty restores all topics in the backup |
topicMapping | map | {} | Source topic to target topic names |
partitionMapping | map | {} | Source partition to target partition IDs |
repartitioning | map | {} | Per-target-topic repartitioning config |
dryRun | bool | false | Validate without executing the restore |
produceBatchSize | int | 1000 | Producer batch size |
produceAcks | int | -1 | -1 for all ISR, 1 for leader, 0 for no acknowledgements |
produceTimeoutMs | int | 30000 | Broker-side produce timeout |
purgeTopics | bool | false | Delete records from target topics before restore |
autoConsumerGroups | bool | false | Load consumer groups from consumer-groups-snapshot.json and reset offsets after restore |
createTopics | bool | false | Create missing target topics |
defaultReplicationFactor | int | unset | Replication factor for created topics |
includeOriginalOffsetHeader | bool | true | Add x-original-offset, x-original-timestamp and x-source-partition headers to every restored record (v1.3.0; this was always on before) |
stripOffsetHeaders | bool | false | Remove the headers kafka-backup added at backup time before producing; with includeOriginalOffsetHeader: false the restored records match the source header-for-header (v1.3.0) |
repartitioning
repartitioning:
restored-orders:
strategy: murmur2 # murmur2 or automatic
targetPartitions: 6
targetPartitions must be greater than zero. strategy must be murmur2 or automatic.
offsetReset
| Field | Type | Required | Description |
|---|---|---|---|
enabled | bool | No | Enable offset reset as part of restore |
consumerGroups | []string | Yes | Consumer groups to reset |
strategy | string | No | manual, auto, or dry_run; defaults to manual |
rollback
| Field | Type | Default | Description |
|---|---|---|---|
snapshotBeforeRestore | bool | true | Create an offset snapshot before restore |
snapshotRetentionHours | int | 24 | Snapshot retention in hours |
autoRollbackOnFailure | bool | false | Roll back offsets automatically on restore failure |
snapshotStorage | object | unset | Optional PVC storage override for snapshots |
template.pod
| Field | Type | Required | Description |
|---|---|---|---|
hostAliases | array | No | Pod-level Kubernetes hostAliases entries injected into restore Job pods. Use this for split-DNS or private endpoint storage access |
Each hostAliases item uses the Kubernetes HostAlias shape:
| Field | Type | Required | Description |
|---|---|---|---|
ip | string | Yes | IP address to write into the pod /etc/hosts file |
hostnames | []string | No | Hostnames mapped to ip |
Examples
Restore From a KafkaBackup
apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaRestore
metadata:
name: restore-orders
spec:
backupRef:
name: production-backup
backupId: "production-backup-20260413-120000"
kafkaCluster:
bootstrapServers:
- kafka:9092
topics:
- orders
createTopics: true
produceAcks: -1
Restore From Direct Storage
apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaRestore
metadata:
name: restore-external
spec:
backupRef:
name: ""
backupId: "external-backup-20260413"
storage:
storageType: s3
s3:
bucket: kafka-backups
region: us-west-2
prefix: production
credentialsSecret:
name: s3-credentials
kafkaCluster:
bootstrapServers:
- kafka:9092
Restore with Consumer Group Snapshot
apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaRestore
metadata:
name: restore-with-groups
spec:
backupRef:
name: production-backup
kafkaCluster:
bootstrapServers:
- dr-kafka:9092
autoConsumerGroups: true
createTopics: true
Restore from Split-DNS Storage
Use template.pod.hostAliases when restore pods need the same private storage endpoint mapping as backup pods.
apiVersion: kafka.oso.sh/v1alpha1
kind: KafkaRestore
metadata:
name: restore-private-s3
spec:
backupRef:
name: production-backup
backupId: "production-backup-20260413-120000"
kafkaCluster:
bootstrapServers:
- dr-kafka:9092
createTopics: true
template:
pod:
hostAliases:
- ip: "10.10.0.5"
hostnames:
- s3.internal
- minio.internal
Status
status:
phase: Completed
message: "Restore completed successfully"
startTime: "2026-04-13T12:00:00Z"
completionTime: "2026-04-13T12:30:00Z"
progressPercent: 100
recordsRestored: 1000000
bytesRestored: 1073741824
segmentsProcessed: 8
offsetMappingPath: "/tmp/offset-mapping.json"
Interrupted restores
A restore runs inside the operator process. If the operator restarts while it runs (OOMKilled, evicted, rolled), the restore is set to phase: Failed with condition reason Interrupted from operator v1.6.5; earlier releases left it Running / "Restore in progress" for ever (#98).
It is not resumed automatically: the operator does not checkpoint restores, so running it again produces the records it had already restored a second time. Records restored before the interruption stay in the target topics. Clear them if you need an exact copy, then change the spec to run the restore again (a metadata-only change such as an annotation does not start a new run).
Next Steps
- KafkaBackup - Create backups
- KafkaOffsetReset - Reset offsets after restore