A Kubernetes operator for KubeVirt that enables file-level restore operations for virtual machines from various backup sources.
The VM File Restore Operator provides a declarative way to restore individual files and directories to KubeVirt VirtualMachines from multiple source types:
- PersistentVolumeClaims (PVCs): Restore from backup PVCs
- VolumeSnapshots: Restore from Kubernetes VolumeSnapshots
- Remote Backups: Restore from remote storage (S3, NFS, etc.)
This operator simplifies disaster recovery and file-level backup scenarios for virtualized workloads running on KubeVirt, enabling granular restore operations without needing to restore entire VM disk images.
- Declarative File Restore: Use Kubernetes CRs to restore files to running VMs
- Multiple Source Types: Restore from PVCs, VolumeSnapshots, or remote storage
- Automatic and Manual Modes: Automatic restore with specified paths, or manual mode for interactive restore
- Hot-plug Technology: No VM restart required - volumes are hot-plugged at runtime
- Guest OS Auto-Detection: Automatically detects Linux/Windows and adjusts mount paths
- SSH-Based Execution: Secure SSH access for executing restore commands in guest OS
- Robust Error Handling: Automatic retries, timeouts, and detailed error reporting
- Idempotent Operations: Safe to retry, handles partial failures gracefully
- HCO Integration: Can be managed by HyperConverged Cluster Operator with TLS security profiles
The operator uses a 9-phase state machine:
New → Init → Hotplugging → WaitingForAttachment → SSHConnecting →
Restoring → Cleanup → Succeeded
↓
Failed
How it works:
- Init: Validates target VM is running and source exists
- Hotplugging: Modifies VM spec to add restore volume (hot-plug)
- WaitingForAttachment: Waits for KubeVirt to attach volume to VMI
- SSHConnecting: Establishes SSH connection to VM guest OS
- Restoring: Executes helper script to mount and restore files
- Cleanup: Unplugs volume from VM, deletes temporary PVCs
- Succeeded/Failed: Terminal state with completion time
Special Mode:
- VolumeReady: Manual restore mode - volume stays attached until CR deletion
- go version v1.26.0+
- docker version 17.03+
- kubectl version v1.11.3+
- Access to a Kubernetes v1.11.3+ cluster with KubeVirt installed
Build and push your image to the location specified by IMG:
make docker-build docker-push IMG=<some-registry>/file-restore-operator:tagNOTE: This image ought to be published in the personal registry you specified. And it is required to have access to pull the image from the working environment. Make sure you have the proper permission to the registry if the above commands don’t work.
Install the CRDs into the cluster:
make installDeploy the Manager to the cluster with the image specified by IMG:
make deploy IMG=<some-registry>/file-restore-operator:tagNOTE: If you encounter RBAC errors, you may need to grant yourself cluster-admin privileges or be logged in as admin.
Create instances of your solution You can apply the samples (examples) from the config/sample:
kubectl apply -k config/samples/NOTE: Ensure that the samples has default values to test it out.
The operator requires SSH access to VMs to execute restore operations. The operator connects as user filerestore on both Linux and Windows VMs.
After deploying the operator, retrieve the public key:
kubectl get configmap vm-file-restore-operator-ssh \
-n file-restore \
-o jsonpath='{.data.ssh-publickey}'Use the automated setup scripts to configure VMs in one command:
For Linux VMs:
# Get the operator's public key
PUB_KEY=$(kubectl get configmap vm-file-restore-operator-ssh -n file-restore \
-o jsonpath='{.data.ssh-publickey}')
# Run setup script via virtctl ssh (as root)
virtctl ssh root@vmi/fedora -c "curl -L https://raw.githubusercontent.com/kubevirt/vm-file-restore-operator/refs/heads/main/guest-helpers/linux/setup.sh | bash -s -- '$PUB_KEY'"For Windows VMs:
# Get the operator's public key
PUB_KEY=$(kubectl get configmap vm-file-restore-operator-ssh -n file-restore \
-o jsonpath='{.data.ssh-publickey}')
# Run setup script via virtctl ssh (as Administrator)
virtctl ssh Administrator@vmi/windows -c "curl -L https://raw.githubusercontent.com/kubevirt/vm-file-restore-operator/refs/heads/main/guest-helpers/windows/setup.bat -o %TEMP%\setup.bat && %TEMP%\setup.bat \"$PUB_KEY\""What the setup scripts do:
- Create the
filerestoreuser with appropriate permissions - Configure SSH key authentication (command-restricted for security)
- Set up passwordless sudo (Linux only, restricted to restore script)
- Download and install the helper script (
filerestore.shorfilerestore.bat) - Verify the installation
Manual setup: If you prefer manual configuration or need to troubleshoot, see guest-helpers/linux/setup.sh and guest-helpers/windows/setup.bat for the detailed steps.
Once SSH is configured and helpers are installed, create a restore:
kubectl apply -f config/samples/restore_v1alpha1_virtualmachinefilerestore.yamlMonitor progress:
kubectl get vmfr -w
kubectl describe vmfr <restore-name>The VirtualMachineFileRestore CRD allows you to specify:
- virtualMachineName: Target VM to restore files into
- source: One of PVC, VolumeSnapshot, or RemoteBackup
- files: List of specific file paths to restore
- directories: List of directories to restore recursively
- targetVolume: (Optional) Specific volume in the VM to restore to
apiVersion: filerestore.kubevirt.io/v1alpha1
kind: VirtualMachineFileRestore
metadata:
name: restore-from-pvc
spec:
virtualMachineName: my-vm
source:
persistentVolumeClaim:
name: backup-pvc
namespace: default
files:
- /etc/important-config.conf
directories:
- /var/lib/dataapiVersion: filerestore.kubevirt.io/v1alpha1
kind: VirtualMachineFileRestore
metadata:
name: restore-from-snapshot
spec:
virtualMachineName: my-vm
source:
volumeSnapshot:
name: vm-snapshot-20260415
files:
- /etc/database/db.conf
targetVolume: data-volumeapiVersion: filerestore.kubevirt.io/v1alpha1
kind: VirtualMachineFileRestore
metadata:
name: restore-from-remote
spec:
virtualMachineName: my-vm
source:
remoteBackup:
url: s3://my-bucket/backups/vm-backup.tar.gz
secretRef:
name: s3-credentials
directories:
- /opt/application/dataNote: Remote sources are planned but not yet implemented.
Omit sourcePath to hotplug the volume without automatic restore. The volume stays attached in VolumeReady phase until you delete the CR:
apiVersion: filerestore.kubevirt.io/v1alpha1
kind: VirtualMachineFileRestore
metadata:
name: manual-restore
spec:
target:
apiGroup: kubevirt.io
kind: VirtualMachine
name: fedora
source:
snapshot:
name: snap1
# No sourcePath - manual modeIn manual mode:
- Volume is hotplugged and mounted at
/backup(Linux) orC:\backup(Windows) - CR stays in
VolumeReadyphase - SSH into VM and manually copy files
- Delete CR to unplug volume and clean up
kubectl get vmfr
kubectl describe vmfr restore-from-pvcThe status shows the current phase and progress:
Phases:
New- CR created, not yet startedInit- Validating target VM and sourceHotplugging- Attaching restore volume to VMWaitingForAttachment- Waiting for volume to attach (max 5 minutes)SSHConnecting- Establishing SSH connection (max 2 minutes with retry)Restoring- Executing file restore commandVolumeReady- Manual restore mode (sourcePath empty), volume is mountedCleanup- Unplugging volume and cleaning upSucceeded- Restore completed successfullyFailed- Restore failed (see errorMessage for details)
Status Fields:
phase- Current phase of the restorestartTime- When the restore startedcompletionTime- When the restore completedrestoredFilesCount- Number of files restoredmountPath- Where the volume is mounted in guest OSerrorMessage- Details if restore failedconditions- Additional status information
Timeouts and Retries:
- Volume attachment: 5-minute timeout with exponential backoff
- SSH connection: 2-minute timeout with retry
- All operations are idempotent and safe to retry
Delete the instances (CRs) from the cluster:
kubectl delete -k config/samples/Delete the APIs(CRDs) from the cluster:
make uninstallUnDeploy the controller from the cluster:
make undeployFollowing the options to release and provide this solution to the users.
- Build the installer for the image built and published in the registry:
make build-installer IMG=<some-registry>/file-restore-operator:tagNOTE: The makefile target mentioned above generates an 'install.yaml' file in the dist directory. This file contains all the resources built with Kustomize, which are necessary to install this project without its dependencies.
- Using the installer
Users can just run 'kubectl apply -f ' to install the project, i.e.:
kubectl apply -f https://raw.githubusercontent.com/<org>/file-restore-operator/<tag or branch>/dist/install.yaml- Build the chart using the optional helm plugin
operator-sdk edit --plugins=helm/v1-alpha- See that a chart was generated under 'dist/chart', and users can obtain this solution from there.
NOTE: If you change the project, you need to update the Helm Chart using the same command above to sync the latest changes. Furthermore, if you create webhooks, you need to use the above command with the '--force' flag and manually ensure that any custom configuration previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' is manually re-applied afterwards.
Run unit tests:
make testEnd-to-end tests run against a kubevirtci cluster with the operator deployed.
Setup kubevirtci cluster:
# Bring up a local Kubernetes cluster with KubeVirt and CDI
# This automatically deploys pinned versions of KubeVirt and kubevirtci for reproducibility
make cluster-up
# Configure kubectl to use the cluster
eval "$(make cluster-kubeconfig)"
# Verify cluster is ready
kubectl get nodesCustomize cluster versions (optional):
# Override KubeVirt version
KUBEVIRT_VERSION=v1.9.0 make cluster-up
# Override kubevirtci tag
KUBEVIRTCI_TAG=<tag> make cluster-up
# Override Kubernetes version
KUBEVIRT_PROVIDER=k8s-1.37 make cluster-up
# Override number of nodes
KUBEVIRT_NUM_NODES=3 make cluster-up
# Override wait timeout for KubeVirt/CDI readiness (default: 10m)
KUBEVIRT_WAIT_TIMEOUT=15m make cluster-upDeploy operator to cluster:
make cluster-syncTo use a custom image:
IMG=<your-registry>/vm-file-restore-operator:tag make cluster-syncRun tests:
# Make sure virtctl is in PATH
make test-e2eTear down cluster when done:
make cluster-downFile Restore Test: The file restore e2e test validates the complete workflow:
- Creates a Fedora VM (10Gi boot disk from quay.io/containerdisks/fedora:44)
- Installs guest helper via SSH with operator's public key
- Creates test user and generates 1GB test file
- Creates a VolumeSnapshot of the boot disk
- Deletes the file to verify restore actually works
- Creates VirtualMachineFileRestore CR
- Polls for restore completion and verifies file size, ownership, and permissions
Run golangci-lint:
make lintFix linting issues automatically:
make lint-fix// TODO(user): Add detailed information on how you would like others to contribute to this project
NOTE: Run make help for more information on all potential make targets
More information can be found via the Kubebuilder Documentation
Copyright 2026.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.