Skip to content

Node Agent and Kopia

The node-agent DaemonSet provides file-level volume backup/restore using Kopia. This is the recommended path for workloads where CSI snapshots are unavailable or too coarse-grained.

Architecture

node-agent runs on every node. When a DataUpload/DataDownload CRD is created, velero-server selects the node-agent instance running on the same node as the pod whose PVC needs backing up.

velero-server
  │  creates DataUpload CRD (spec.node = "node-A")
node-agent pod on node-A
  │  DataUploadController reconciles
  │  mounts PVC via hostPath: /var/lib/kubelet/pods/.../volumes/...
  ▼ (Kopia client)
Kopia Repository on ObjectStore (BSL)

node-agent must run on the same node as the pod whose PVC it's backing up, because it accesses the PVC via hostPath through the kubelet volume manager paths.

Kopia fundamentals

Kopia is a content-addressed, deduplicated backup engine.

Velero embeds it as a library (not as an external binary, unlike the old Restic integration).

Here are some key concepts:

Repository: The central storage entity. Velero creates one Kopia repository per BSL, shared by all node-agent instances. Repository access requires a password stored in a Kubernetes Secret (velero-repo-credentials by default).

Snapshot: Each DataUpload creates one Kopia snapshot: a versioned tree of content hashes. Deduplication happens content-addressably across snapshots: unchanged blocks are never re-uploaded.

Content-Addressable Storage: File data is chunked, hashed (BLAKE3 or SHA256), and stored by hash. If two pods have identical files, those bytes are stored once. This makes incremental backups very efficient for large PVCs with small change sets.

Repository maintenance: Kopia repositories accumulate unreferenced content when old snapshots are deleted. maintenance run (full/quick) prunes this. Velero runs maintenance automatically via BackupRepositoryController, but you need to understand it for large-scale deployments where maintenance can lag.

DataUpload and DataDownload CRDs

These CRDs are created by velero-server and reconciled by node-agent. They carry the specifics of what to upload/download and status back to velero-server.

Field Type Description
spec.snapshotType string CSI (current) or Restic (legacy). Determines which uploader is used.
spec.sourceNamespace string Namespace of the PVC being backed up.
spec.sourcePVC string Name of the PVC. node-agent resolves this to a host path.
spec.backupStorageLocation string Which BSL to write Kopia repository data to.
spec.operationTimeout duration How long node-agent waits before failing the upload. Tune for large PVCs.
spec.node string Target node name. node-agent only reconciles DataUploads targeted at its own node.
status.snapshotID string Kopia snapshot ID written by node-agent on success. Used by DataDownload to restore.
status.progress Progress BytesDone / TotalBytes: live progress from the Kopia upload stream.

node-agent Configuration

# ConfigMap velero/node-agent
apiVersion: v1
kind: ConfigMap
metadata:
  name: node-agent
  namespace: velero
data:
  # Max concurrent Kopia operations per node
  concurrentRepoOperations: "3"
  # Kopia upload workers per operation
  parallelFilesUpload: "10"
  # Timeout for acquiring repository lock
  backupRepositoryLockCheckTimeout: "1m"
  # How many maintenance records to keep
  keepLatestMaintenance: "3"

Kopia Repository Password Management

# Default secret created by velero install
kubectl get secret -n velero velero-repo-credentials -o yaml

# Rotate the password (requires all node-agents to restart to pick up the new secret)
kubectl create secret generic velero-repo-credentials \
  --from-literal=repository-password="$(openssl rand -base64 32)" \
  --dry-run=client -o yaml | kubectl apply -f -

Repository Password Loss

If the velero-repo-credentials secret is lost and no backup of it exists, the Kopia repository is permanently unreadable. Include this secret in your cluster secrets backup strategy.

Monitoring Uploads

# Watch DataUpload objects for active backups
kubectl get dataupload -n velero -w

# Progress for a specific upload
kubectl get dataupload -n velero my-upload -o jsonpath='{.status.progress}'

# node-agent logs on the relevant node
kubectl logs -n velero -l name=node-agent --field-selector spec.nodeName=<node-name>

Restic Migration

If you're on a pre-v1.13 deployment using Restic:

# Migrate existing Restic backups to Kopia-readable format
velero backup-location set default --provider velero.io/aws  # or your provider
# Restic repos and Kopia repos are incompatible — old Restic backups
# can still be RESTORED with the Restic uploader even after switching to Kopia
# for new backups. Set spec.uploaderType: restic on the restore if needed.

Next Up

CSI Snapshots