Plugin System
Velero's plugin system is its most powerful extension point. Understanding the interfaces and process model is essential for both implementing plugins and contributing to core.
Plugin types¶
| Type | Version | Purpose |
|---|---|---|
ObjectStore |
v1 | Read/write/list for a storage backend. Every deployment needs one. External only. |
VolumeSnapshotter |
v1 | Cloud-provider-specific volume snapshots. Legacy path. External only. |
BackupItemAction |
v1, v2 | Runs per-item during backup. Can mutate, skip, or expand the item graph. |
RestoreItemAction |
v1, v2 | Runs per-item during restore. Can mutate items before they're applied to the cluster. |
DeleteItemAction |
v1 | Runs when a backup is deleted. Allows plugins to clean up associated cloud resources. |
ItemBlockAction |
v1 | Groups related items for atomic processing (e.g. PVC + its PV). |
v1 → v2 Evolution¶
V2 plugin interfaces add async operation support, critical for long-running operations like CSI data movement:
Execute()returns anoperationIDfor long-running workProgress(operationID)polls operation statusCancel(operationID)cancels in-flight operations- RestoreItemAction v2 adds
AreAdditionalItemsReady()for dependency waiting andSkipRestoreflag
V1 plugins are automatically adapted to the V2 interface — they work transparently with empty operation IDs and no async support.
ObjectStore interface¶
// pkg/plugin/velero/object_store.go
type ObjectStore interface {
// Init is called once with the config map from BSL.spec.config
Init(config map[string]string) error
// PutObject writes the given body to bucket/key
PutObject(bucket, key string, body io.Reader) error
// ObjectExists returns true if bucket/key exists
ObjectExists(bucket, key string) (bool, error)
// GetObject returns the body of bucket/key
GetObject(bucket, key string) (io.ReadCloser, error)
// ListCommonPrefixes returns all "directories" under prefix/delimiter
ListCommonPrefixes(bucket, prefix, delimiter string) ([]string, error)
// ListObjects returns all keys with given prefix
ListObjects(bucket, prefix string) ([]string, error)
// DeleteObject deletes the object at bucket/key
DeleteObject(bucket, key string) error
// CreateSignedURL returns a pre-signed URL for the object
CreateSignedURL(bucket, key string, ttl time.Duration) (string, error)
}
BackupItemAction interface¶
// pkg/plugin/velero/backup_item_action.go
type BackupItemAction interface {
// AppliesTo returns what resource types this action handles
AppliesTo() (ResourceSelector, error)
// Execute is called for each matching item during backup.
// Returns the (possibly mutated) item and any additional items to back up.
Execute(item runtime.Unstructured, backup *api.Backup) (
updatedItem runtime.Unstructured,
additionalItems []ResourceIdentifier,
error,
)
}
type ResourceSelector struct {
IncludedNamespaces []string
ExcludedNamespaces []string
IncludedResources []string // "pods", "persistentvolumeclaims"
ExcludedResources []string
LabelSelector string
}
RestoreItemAction interface¶
// pkg/plugin/velero/restore_item_action.go
type RestoreItemAction interface {
AppliesTo() (ResourceSelector, error)
// Execute is called for each item during restore.
// Returns the (possibly mutated) item, any additional items to restore,
// and whether to skip this item entirely.
Execute(input *RestoreItemActionExecuteInput) (*RestoreItemActionExecuteOutput, error)
}
type RestoreItemActionExecuteInput struct {
Item runtime.Unstructured // item from the backup
ItemFromBackup runtime.Unstructured // same item, always unmodified
Restore *api.Restore
}
type RestoreItemActionExecuteOutput struct {
UpdatedItem runtime.Unstructured
AdditionalItems []ResourceIdentifier
SkipRestore bool
}
Implementing a Plugin¶
Use velero-plugin-example as a scaffold.
// main.go
func main() {
veleroplugin.NewServer().
// Register all plugin types your binary implements
RegisterObjectStore("my.company/my-store", newMyObjectStore).
RegisterBackupItemAction("my.company/strip-secrets", newStripSecretsAction).
RegisterRestoreItemAction("my.company/remap-storageclass", newRemapStorageClass).
Serve()
}
func newMyObjectStore(logger logrus.FieldLogger) (interface{}, error) {
return &MyObjectStore{logger: logger}, nil
}
// MyObjectStore implements the ObjectStore interface
type MyObjectStore struct {
logger logrus.FieldLogger
client *minio.Client
}
func (o *MyObjectStore) Init(config map[string]string) error {
endpoint := config["endpoint"]
// ... initialize minio client
return nil
}
Plugin Deployment Model¶
Plugins are bundled into the velero pod via an init container pattern.
The init container copies the plugin binary to a shared EmptyDir volume
mounted at /plugins.
initContainers:
- name: velero-plugin-for-aws
image: velero/velero-plugin-for-aws:v1.10.0
volumeMounts:
- name: plugins
mountPath: /target
command: ["/bin/sh", "-c", "cp /velero-plugin-for-aws /target/"]
volumes:
- name: plugins
emptyDir: {}
containers:
- name: velero
volumeMounts:
- name: plugins
mountPath: /plugins # velero scans this directory at startup
Velero scans /plugins at startup and registers all binaries it finds. Each
binary is invoked via go-plugin's handshake protocol to discover its
capabilities.
Plugin Versioning¶
Plugin gRPC protocol is versioned. When upgrading Velero, check the
compatibility matrix in the plugin's README. Breaking changes to plugin
interfaces are rare but happen across minor versions. Watch for interface
changes in pkg/plugin/velero/.
Built-In BackupItemActions¶
Velero ships several built-in BIAs worth knowing:
| Name | Applies to | What it does |
|---|---|---|
pod-action |
pods |
Adds the pod's PVCs (and their PVs) to the backup graph |
pvc-action |
persistentvolumeclaims |
Adds the bound PV; triggers volume backup decision |
csi-pvc-backupitem-action |
persistentvolumeclaims |
Creates VolumeSnapshot for CSI-backed PVCs |
service-account-action |
serviceaccounts |
Includes secrets referenced by the SA |
role-bindings-action |
rolebindings |
Includes referenced Roles/ClusterRoles |
Built-in RestoreItemActions¶
| Name | Applies to | What it does |
|---|---|---|
job-action |
jobs |
Removes spec.selector and spec.template.labels so the job controller re-generates them |
service-action |
services |
Strips spec.clusterIP, spec.clusterIPs, spec.nodePort |
serviceaccount-action |
serviceaccounts |
Strips auto-generated secret token references |
csi-volumesnapshot-restore-action |
volumesnapshots |
Handles VolumeSnapshot object restoration binding |
csi-volumesnapshotcontent-restore-action |
volumesnapshotcontents |
Handles VolumeSnapshotContent with the original snapshot handle |