Plugins¶
Overview¶
Plugins allow extending KubeVirt with out-of-tree functionality. A Plugin is a cluster-scoped Custom Resource that defines domain hooks, node hooks, and admission references.
The Plugins feature is currently in Alpha (introduced in v1.9) and requires the Plugins feature gate.
Enabling the feature gate¶
Enable the Plugins feature gate by following the steps in
Activating feature gates.
Domain Hooks¶
Domain hooks modify the libvirt domain XML before it is applied. There are two types: CEL hooks for pure domain mutation, and sidecar hooks for cases that require reading external state (files, APIs, license servers) to feed into domain XML mutation.
Hooks are applied in declaration order within each plugin. Across plugins, hooks are applied in alphabetical order by plugin name.
Legacy annotation-based sidecar hooks run before plugin hooks.
CEL expressions¶
A CEL expression that transforms the domain XML inline. The expression uses
vmi and domainSpec variables and must return a Domain{...} struct using
libvirtxml type names. The result is deep-merged with the generated domain -
only fields mentioned in the expression are overwritten, and unmentioned fields
are left untouched. Slices are replaced wholesale, not merged element-by-element.
apiVersion: plugin.kubevirt.io/v1alpha1
kind: Plugin
metadata:
name: cel-domain-example
spec:
domainHooks:
- cel:
expression: 'Domain{CPU: DomainCPU{Mode: "host-passthrough"}}'
Sidecar¶
A sidecar container in virt-launcher communicates via gRPC over a Unix socket. The sidecar receives the domain spec and returns a modified version. Use sidecar hooks when you need logic beyond what CEL can express, such as reading external state or making API calls.
The Plugin CR references the socket path. To inject the sidecar container into the virt-launcher pod, set up a MutatingAdmissionPolicy (or webhook). This gives you full control over the sidecar container spec (image, resources, security context, volumes).
apiVersion: plugin.kubevirt.io/v1alpha1
kind: Plugin
metadata:
name: my-plugin
spec:
domainHooks:
- sidecar:
socketPath: "/var/run/kubevirt-plugin/my-plugin/hook.sock"
Socket path constraints:
- The socket must reside under
/var/run/kubevirt-plugin/<plugin-name>/, defined as a shared volume with the compute container. - Symlinks are not allowed in socket paths.
- Sidecar volume mounts must use
subPathto ensure each plugin has isolated access to its own directory.
Node Hooks¶
Node hooks execute during VM lifecycle events via gRPC from virt-handler to a plugin server. The plugin author deploys and manages this server as a DaemonSet, giving the cluster admin control over its capabilities and resources via standard Kubernetes mechanisms.
Available hook points:
PreVMStart,PostVMStartPreVMStop,PostVMStopPreMigrationSource,PreMigrationTarget,PostMigrationTarget
Node hooks may fire multiple times for the same lifecycle event due to reconciliation retries. Implementations must be idempotent.
apiVersion: plugin.kubevirt.io/v1alpha1
kind: Plugin
metadata:
name: node-hook-example
spec:
nodeHooks:
- socket: /var/run/my-plugin/hook.sock
permittedHooks:
- PreVMStart
- PreVMStop
Admission References¶
Plugins can reference Kubernetes admission policies and webhooks for pod/VMI modifications:
apiVersion: plugin.kubevirt.io/v1alpha1
kind: Plugin
metadata:
name: admission-example
spec:
mutatingAdmissionPolicies:
- name: "my-mutating-policy"
validatingAdmissionPolicies:
- name: "my-validating-policy"
mutatingAdmissionWebhooks:
- name: "my-mutating-webhook"
validatingAdmissionWebhooks:
- name: "my-validating-webhook"
These are listed in the Plugin CR to provide a central place to understand all plugin components.
Failure Handling and Conditions¶
failureStrategy:Fail(default) blocks the operation on error.Ignorelogs the error and continues. Can be set at plugin level as the default for all hooks, and overridden per hook.condition: A CEL expression that filters which VMIs the plugin applies to. Can be set at plugin level and per hook. Per-hook conditions further narrow the plugin-level filter.timeout: Per-hook maximum wait duration. Defaults to 30s.
This example targets live migration: the plugin-level condition restricts hooks to VMIs that are already running (i.e. migration targets), and the hook-level condition further narrows to VMIs that are live-migratable.
apiVersion: plugin.kubevirt.io/v1alpha1
kind: Plugin
metadata:
name: full-example
spec:
failureStrategy: Fail
condition: "vmi.status.phase == 'Running'"
domainHooks:
- cel:
expression: 'Domain{Memory: DomainMemory{Value: uint(2048), Unit: "GiB"}}'
failureStrategy: Ignore
condition: "vmi.status.conditions.exists(c, c.type == 'LiveMigratable' && c.status == 'True')"
timeout: 10s
Developing Plugins¶
Sidecar domain hooks and node hooks communicate via gRPC. The proto definitions
can be found at
pkg/hooks/plugins/v1alpha1/api.proto
in the KubeVirt repository.
- Domain hooks implement the
DomainHookService.MutateDomainRPC. The request contains the domain XML, the VMI object (as JSON), and a sidecar context with the invocation context (Boot, MigrationSource, or MigrationTarget). - Node hooks implement the
NodeHookService.ExecuteNodeHookRPC. The request contains the hook point name, the VMI object (as JSON), and node context.
Related¶
- Hook Sidecar Container - legacy mechanism (deprecated).
- VEP 190 - design details.