This is the documentation for the latest development version of Velero. Both code and docs may be unstable, and these docs are not guaranteed to be up to date or correct. See the latest version.
This guide explains how to use Velero’s fine-grained restore filters: per-namespace, per-kind rules with independent label selectors and resource name patterns. Configuration lives in a ResourcePolicy ConfigMap, using the exact same format introduced for fine-grained backup filters.
For architecture and pipeline details, see the design document.
Velero’s traditional restore filters apply the same namespace list, resource types, and label selector to every namespace being restored. Common scenarios need more control:
app-config and app-secret without restoring monitoring-config from the same namespace.Fine-grained filters add two optional sections to the ResourcePolicy ConfigMap:
| Section | Scope | Behavior |
|---|---|---|
namespacedFilterPolicies |
Namespaces you match (exact name or glob) | Exclusive allowlist — only resource kinds listed in resourceFilters (or covered by a catch-all) are restored for those namespaces, provided they pass global filters. |
clusterScopedFilterPolicy |
Cluster-scoped resources globally | Refinement overlay — listed kinds get per-kind label and name rules; unlisted cluster-scoped kinds still use global RestoreSpec filters. |
Backward compatible: Fine-grained restore filters are optional. If a restore does not reference a ResourcePolicy, Velero relies solely on standard RestoreSpec filters (includedNamespaces, includedResources, labelSelector, etc.).
velero by default).Every example below follows the same three steps:
data.policy containing version: v1 and your filter rules.velero restore describe and inspect the restored resources.Use this once; later examples show only the policy: body.
ResourcePolicy ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: my-restore-filter-policy
namespace: velero
data:
policy: |
version: v1
namespacedFilterPolicies:
- namespaces:
- my-namespace
resourceFilters:
- kinds: [ConfigMap]
labelSelector:
matchLabels:
app: my-app
Restore:
apiVersion: velero.io/v1
kind: Restore
metadata:
name: my-restore
namespace: velero
spec:
backupName: my-backup
includedNamespaces:
- my-namespace
resourcePolicy:
kind: configmap
name: my-restore-filter-policy
CLI equivalent:
velero restore create my-restore \
--from-backup my-backup \
--include-namespaces my-namespace \
--resource-policies-configmap my-restore-filter-policy
Verify:
velero restore describe my-restore
The restore pipeline evaluates global resource filters first:
RestoreSpec.IncludedResources and RestoreSpec.ExcludedResources act as a global gate.secrets, listing Secret in a namespace policy will have no effect.Each example includes: goal, policy YAML, restore notes, and expected outcome.
Goal: Confirm that namespaces without a namespacedFilterPolicies entry still use global RestoreSpec filters.
Policy: Omit namespacedFilterPolicies and clusterScopedFilterPolicy entirely.
Restore:
spec:
includedNamespaces:
- ns-a
- ns-b
# No resourcePolicy — global filters only
Expected outcome: All resources in included namespaces follow includedNamespaces, labelSelector, includedResources, and related global fields — same as before this feature.
Goal: In ns-a, restore only ConfigMaps, Secrets, Deployments, and Pods with app=my-app. In ns-b, use global filters (no policy entry for that namespace).
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
resourceFilters:
- kinds: [ConfigMap, Secret, Deployment, Pod]
labelSelector:
matchLabels:
app: my-app
Restore:
spec:
includedNamespaces:
- ns-a
- ns-b
resourcePolicy:
kind: configmap
name: per-namespace-resource-filter-policy
Expected outcome:
app=my-app (e.g. app-config, app-secret, app-deployment). Resources like monitoring-config (different labels) are excluded.Goal: Restore only two ConfigMaps by exact name, optionally requiring a label.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- target-namespace
resourceFilters:
- kinds: [ConfigMap]
names: [vm-1, vm-2]
labelSelector:
matchLabels:
resource-type: VirtualMachine
Expected outcome: Only vm-1 and vm-2 ConfigMaps with resource-type=VirtualMachine are restored. vm-3 and other ConfigMaps are skipped.
Goal: Restore app-* ConfigMaps and Secrets in production, but exclude temporary and debug names.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- production
resourceFilters:
- kinds: [ConfigMap, Secret]
names: ["app-*"]
excludedNames: ["*-tmp-*", "*-debug-*", "*-tmp", "*-debug"]
Expected outcome:
app-config, app-cache-config, app-secretapp-config-tmp, app-tmp-config, app-debug-config, monitoring-tmp-secretexcludedNames takes precedence over names when both match.
Goal: Apply different label rules to different resource types in the same namespace.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- target-namespace
resourceFilters:
- kinds: [ConfigMap]
orLabelSelectors:
- matchLabels:
app: production-workload-1
component: vm-group
- matchLabels:
app: production-workload-2
component: vm-service
Expected outcome: ConfigMaps matching either label combination are restored; other ConfigMaps in the namespace are not.
Note: Prefer matchExpressions with In for value-OR on a single key (see next example). Use orLabelSelectors when you need OR across independent multi-key groups. labelSelector and orLabelSelectors cannot appear in the same resourceFilters entry.
matchExpressions)Goal: Restore Deployments and Pods that are in prod or staging, belong to app=my-app, and do not carry a skip label.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- production
resourceFilters:
- kinds: [Deployment, Pod]
labelSelector:
matchLabels:
app: my-app
matchExpressions:
- key: environment
operator: In
values: [prod, staging]
- key: do-not-restore
operator: DoesNotExist
Supported operators: In, NotIn, Exists, DoesNotExist (same as Kubernetes / Velero global --selector).
Other useful patterns:
# Exclude environments
matchExpressions:
- key: environment
operator: NotIn
values: [dev, test]
# Require a label key to be present (any value)
matchExpressions:
- key: tier
operator: Exists
Expected outcome: Only Deployments/Pods with app=my-app, environment in {prod, staging}, and without do-not-restore are restored.
Goal: Restore ConfigMaps, Secrets, or Deployments that match any of several label conditions.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
resourceFilters:
- kinds: [ConfigMap, Secret]
orLabelSelectors:
- matchLabels:
app: my-app
- matchLabels:
app: monitoring
- kinds: [Deployment]
orLabelSelectors:
- matchLabels:
app: my-app
- matchLabels:
app: monitoring
- matchLabels:
component: backend
Expected outcome: Resources included if they match any selector in orLabelSelectors for their kind (AND within each selector, OR across the list).
Goal: Combine exact names with OR label selectors for a single kind.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- target-namespace
resourceFilters:
- kinds: [ConfigMap]
names: [vm-1, vm-2]
orLabelSelectors:
- matchLabels:
resource-type: VirtualMachine
- matchLabels:
component: vm-group
- matchLabels:
component: vm-service
Expected outcome: Only vm-1 and vm-2 that also satisfy one of the label OR branches.
Goal: Apply the same rules to ns-a, ns-b, and production in a single policy block.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
- ns-b
- production
resourceFilters:
- kinds: [ConfigMap]
- kinds: [Deployment]
labelSelector:
matchLabels:
tier: web
Expected outcome:
tier=web only.Goal: Different restore breadth for team-frontend-prod, team-frontend-dev, and team-backend-test using glob patterns.
Note on Precedence: Exact namespace matches always take precedence regardless of where they are listed. However, if multiple glob patterns could match a namespace, they are evaluated in the order they appear. Always list specific globs before broad globs.
Policy:
version: v1
namespacedFilterPolicies:
# Globs must be ordered specific-to-broad
- namespaces:
- "team-frontend-*" # specific pattern match
resourceFilters:
- kinds: [Deployment, Service, ConfigMap]
- namespaces:
- "team-*" # broad pattern
resourceFilters:
- kinds: [Deployment, Service]
# Exact matches always win, even if placed at the bottom
- namespaces:
- team-frontend-prod # exact match
resourceFilters:
- kinds: [Deployment, Service, ConfigMap, Secret, PersistentVolumeClaim]
Expected outcome:
| Namespace | Matched policy | Kinds restored |
|---|---|---|
team-frontend-prod |
team-frontend-prod (Exact match priority) |
5 kinds |
team-frontend-dev |
team-frontend-* (First matching glob) |
3 kinds |
team-backend-test |
team-* (First matching glob) |
2 kinds |
Velero uses first-match semantics: the first policy entry whose namespace pattern matches wins.
Goal: Restore any resource kind that has a given label, without listing every kind. Kind-specific entries override the catch-all.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
resourceFilters:
- kinds: ["*"] # catch-all
labelSelector:
matchLabels:
app: common-app
- kinds: [ConfigMap, Secret] # override for these kinds
labelSelector:
matchLabels:
app: specialized-app
Rules:
names or excludedNames.RestoreSpec.LabelSelector.Expected outcome: ConfigMaps and Secrets use app=specialized-app; all other kinds listed only via catch-all use app=common-app.
Goal: Pin critical Deployments and Secrets by exact name; restore everything else with a label convention.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
resourceFilters:
- kinds: [Deployment]
names: [api-server, worker]
- kinds: [Secret]
names: [db-credentials, tls-cert]
- kinds: ["*"]
labelSelector:
matchLabels:
restore: "true"
Expected outcome:
api-server and workerdb-credentials and tls-certrestore=true onlyGoal: Apply a strict name filter to one kind while restoring all other kinds without listing them or adding labels.
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
resourceFilters:
- kinds: [Secret]
names: [app-secret]
- kinds: ["*"] # no labelSelector — all other kinds included
Expected outcome:
app-secretns-a: all instances restored (subject to global filters)Goal: Refine which cluster-scoped resources are restored by name and label, without replacing global cluster-scoped inclusion.
Policy:
version: v1
clusterScopedFilterPolicy:
resourceFilters:
- kinds: [StorageClass]
names: ["my-app-*"]
- kinds: [ClusterRole, ClusterRoleBinding]
labelSelector:
matchLabels:
app: my-app
Restore (required): You must still include cluster-scoped kinds on the Restore:
spec:
includeClusterResources: true
resourcePolicy:
kind: configmap
name: cluster-scoped-filter-policy
Expected outcome:
my-app-* onlyapp=my-app onlyDifferences from namespace policies:
kinds: [] or kinds: ["*"] is invalid and fails validation.ExcludedResources and namespace filtersGoal: Understand that global exclusions cannot be overridden per namespace.
Restore:
spec:
excludedResources:
- secrets
Policy:
version: v1
namespacedFilterPolicies:
- namespaces:
- ns-a
resourceFilters:
- kinds: [ConfigMap, Secret, Deployment]
labelSelector:
matchLabels:
app: my-app
Result: No Secrets are restored — the namespace policy cannot re-include a globally excluded kind. Velero logs a warning at restore start if you list an excluded kind in namespacedFilterPolicies.
Goal: Understand why you cannot use a single ConfigMap for both backup and restore operations if it contains backup-specific policies.
Policy:
version: v1
volumePolicies:
- conditions:
capacity: "0,10Gi"
action:
type: fs-backup
namespacedFilterPolicies:
- namespaces:
- production
resourceFilters:
- kinds: [ConfigMap, Secret]
names: ["app-*"]
Expected outcome: The restore operation will fail validation. The Velero restore pipeline strictly rejects any ResourcePolicy ConfigMap containing volumePolicies or includeExcludePolicy. To avoid this, the restore-side ConfigMap should contain only the restore-supported sections (namespacedFilterPolicies and/or clusterScopedFilterPolicy).
velero.io/exclude-from-backup=true always winsGoal: Ensure explicitly excluded resources never appear in the restore.
If a resource was backed up (perhaps before the label was added, or manually modified in the archive) but has velero.io/exclude-from-backup: "true", the restore pipeline honors it. Any item carrying this label is skipped regardless of whether it matches global or per-namespace restore filters.
resourceFilters fields| Field | Description |
|---|---|
kinds |
Resource type names (e.g. ConfigMap, deployments). Empty or ["*"] = catch-all (namespace policies only). |
labelSelector |
Kubernetes-style selector with matchLabels and/or matchExpressions (In, NotIn, Exists, DoesNotExist). All requirements are AND-ed. |
orLabelSelectors |
List of selectors; match if any entry matches (AND within each, OR across the list). Use for OR of multi-key groups; prefer In for value-OR on one key. Mutually exclusive with labelSelector. |
names |
Exact names or glob patterns to include. |
excludedNames |
Patterns to exclude; wins over names when both match. |
Name and namespace patterns use the same glob style as elsewhere in Velero (gobwas/glob):
*, ?, [abc], [a-z]**, regex, |, (), !, {}, ,Examples: app-*, team-frontend-*, *-tmp.
Namespaces
RestoreSpec.ExcludedNamespaces — excluded namespaces are never restored.namespacedFilterPolicies — first matching pattern (exact match checked before globs in pattern order).Namespace-scoped resources (when a namespace policy matches)
RestoreSpec.IncludedResources / ExcludedResources apply first.resourceFilters (or catch-all) are allowlisted for restoration.labelSelector / orLabelSelectors replace global selectors.names / excludedNames filter by resource name.velero.io/exclude-from-backup=true always excludes.Cluster-scoped resources
includeClusterResources).clusterScopedFilterPolicy lists the kind, apply its label and name rules.clusterScopedFilterPolicy, use global RestoreSpec filters.velero.io/exclude-from-backup=true always excludes.| Rule | Detail |
|---|---|
| Syntax | kinds: ["*"] or kinds: [] |
| Count | At most one catch-all per namespacedFilterPolicies entry |
| Names | names / excludedNames not allowed on catch-all |
| Override | Kind-specific entries take precedence over catch-all |
| Label inheritance | Does not use RestoreSpec.LabelSelector |
| Cluster-scoped | Catch-all not supported in clusterScopedFilterPolicy |
velero restore describe RESTORE_NAME
velero restore logs RESTORE_NAME
The output of velero restore describe will show the Resource Policy field if a ConfigMap was used.
| Symptom | Likely cause | Fix |
|---|---|---|
Fewer resources than expected in team-frontend-prod |
Broad namespace pattern listed before specific one | Reorder policies: most specific namespaces first |
| Namespace policy lists Secrets but none restored | RestoreSpec.ExcludedResources excludes secrets globally |
Remove global exclusion or accept no Secrets |
ClusterRole in namespace policy has no effect |
Cluster-scoped kind in namespacedFilterPolicies |
Move rule to clusterScopedFilterPolicy; check logs for warning |
| Catch-all does not use restore-wide label | By design | Set labelSelector on the catch-all entry |
Cluster-scoped policy validation error on kinds: ["*"] |
Catch-all not allowed for cluster policy | List each cluster-scoped kind explicitly |
kubectl logs -n velero deployment/velero | grep -i "namespacedFilterPolicies\|clusterScopedFilterPolicy"
kubectl logs -n velero deployment/velero | grep "globally excluded by RestoreSpec.ExcludedResources"
Velero validates the ResourcePolicy when a restore starts. Common errors:
| Error (summary) | Cause |
|---|---|
at least one namespace must be specified |
Empty namespaces: [] |
at least one resourceFilter must be specified |
Empty resourceFilters: [] |
names or excludedNames cannot be specified for catch-all filters |
Name patterns on catch-all entry |
only one catch-all resource filter is allowed |
Multiple catch-alls in one policy entry |
kind "X" appears in both resourceFilters[...] |
Same kind in two entries |
labelSelector and orLabelSelectors cannot co-exist |
Both set in one entry |
invalid label selector |
Bad operator, values, or label key/value syntax |
duplicate namespace pattern |
Same namespace string in two policy entries |
invalid glob pattern |
Bad characters in namespace or name pattern |
clusterScopedFilterPolicy... kinds must be specified (catch-all is not supported) |
Empty or ["*"] kinds in cluster policy |
excludedNames narrows names — e.g. names: ["app-*"] + excludedNames: ["app-config"] excludes app-config only.To help you get started, see the documentation.