Filesystem (PVC or inline NFS)
The filesystem backend stores the kopia repository on a local path that Kopiur mounts into the mover. Behind that path is either a PersistentVolumeClaim or an inline NFS export, which is volume.nfs and needs no PVC. Either way it is typically a NAS or NFS share. See Inline NFS below.
There are no object-store credentials here. The only secret is KOPIA_PASSWORD. What bites people on this backend is ownership, not authentication.
Reach for this when your off-site copy is an on-prem NAS, or any ReadWriteMany volume. For a remote server reached over SSH, see SFTP.
Provider prerequisites
Section titled “Provider prerequisites”- Storage the mover can mount read-write: either a
PersistentVolumeClaimor an NFS export. For a PVC, useReadWriteMany, which an NFS or NAS StorageClass provides. Backup, restore, and maintenance movers may run as different Jobs at the same time, and they all need to mount it. The example bundles a PVC; the inline-NFS variant needs no PVC at all. - The repository path must be writable by the UID the mover runs as, which defaults to
65532. See Troubleshooting.
The Secret shape
Section titled “The Secret shape”Filesystem backends need only the repository encryption password.
| Secret key | Required | What it is |
|---|---|---|
KOPIA_PASSWORD |
yes | The repository encryption password. No backend auth keys. |
stringData: KOPIA_PASSWORD: "choose-something-long-and-random"The Repository
Section titled “The Repository”---apiVersion: v1kind: Secretmetadata: name: fs-repo-creds namespace: backupstype: OpaquestringData: # Filesystem backends need ONLY the repository encryption password. KOPIA_PASSWORD: "choose-something-long-and-random"---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: nas-repo namespace: backupsspec: accessModes: ["ReadWriteMany"] # the mover Job mounts this read-write resources: requests: storage: 500Gi # storageClassName: your-nfs-or-nas-class---apiVersion: kopiur.home-operations.com/v1alpha1kind: Repositorymetadata: name: nas-primary namespace: backupsspec: backend: filesystem: path: /repo # mount path INSIDE the mover pod # `volume` is what backs `path`: a PVC (here) or an inline NFS export # (see nfs.yaml). Omit `volume` entirely if `path` already exists on the # node/image (a hostPath/baked-in mount). volume: pvc: name: nas-repo # the PVC mounted read-write at `path` encryption: passwordSecretRef: name: fs-repo-creds key: KOPIA_PASSWORD create: enabled: trueFields reference (backend.filesystem)
Section titled “Fields reference (backend.filesystem)”| Field | Required | Default | Example | What it controls |
|---|---|---|---|---|
path |
yes | — | /repo |
Mount path inside the mover pod where kopia writes the repository. This is not a path on your NAS; the volume decides what’s behind it. |
volume |
no | — | — | What backs path: exactly one of pvc or nfs. Omit it entirely only if path already exists on the node or image, as a hostPath. |
volume.pvc.name |
— | — | nas-repo |
The PersistentVolumeClaim mounted read-write at path. It must be ReadWriteMany, because movers overlap. |
volume.nfs.server |
— | — | nas.lan |
NFS server hostname or IP, for an inline NFS export with no PVC. See below. |
volume.nfs.path |
— | — | /export/kopia |
The absolute export path on the NFS server (what showmount -e nas.lan lists). |
Customization — the values you actually change
Section titled “Customization — the values you actually change”volume.pvc.namenames the PVC to mount. Its size and StorageClass live on the PVC, not here.volume.nfspoints straight at an NFS export instead of a PVC. See Inline NFS below.pathis the in-pod mount point./repois a fine default.- The mover
securityContextis where you setrunAsUserandfsGroupon the consumingSnapshotPolicy, to match the share’s ownership. See Permissions. create.enabledinitializes the repository if it’s missing.
Sizing the PVC
Section titled “Sizing the PVC”The bundled example requests 500Gi as a placeholder. Size yours to the deduplicated, compressed repository, not to the raw source data.
kopia content-addresses everything, so N daily snapshots of slowly-changing data cost roughly one full copy plus the churn, not N copies. A reasonable starting point is 1 to 1.5 times the source data. Watch actual usage after the first retention cycle and resize; most NAS-backed StorageClasses support volume expansion.
The failure mode to avoid is running the volume completely full, because kopia maintenance needs headroom to rewrite and compact blobs.
Preparing the export (NFS-side ownership)
Section titled “Preparing the export (NFS-side ownership)”The mover runs as UID 65532 by default and is not root, so the classic root_squash setting on the export doesn’t matter here. What matters is that UID 65532 can write the directory:
# on the NAS / NFS server$ mkdir -p /export/kopia$ chown -R 65532:65532 /export/kopiaYour NAS may force all clients to one identity, through all_squash or a “map all users” setting. In that case point the mapping, meaning anonuid and anongid, at the directory owner. Or set the mover’s runAsUser to whatever UID the NAS expects. The full decision table is in Permissions, UID & GID.
Inline NFS (no PVC)
Section titled “Inline NFS (no PVC)”kopia has no native NFS backend. You reach NFS through the filesystem backend, by mounting the export at path.
Instead of pre-creating a ReadWriteMany PVC, name an NFS export directly under volume.nfs. The operator then builds a Kubernetes inline nfs volume on every mover Job: bootstrap, backup, restore, and maintenance.
---apiVersion: v1kind: Secretmetadata: name: nfs-repo-creds namespace: backupstype: OpaquestringData: # Filesystem/NFS backends need ONLY the repository encryption password — the # data sits on your NAS but kopia still encrypts it. No object-store keys. KOPIA_PASSWORD: "choose-something-long-and-random"---apiVersion: kopiur.home-operations.com/v1alpha1kind: Repositorymetadata: name: nas-primary namespace: backupsspec: backend: filesystem: path: /repo # mount path INSIDE the mover pod where kopia writes the repo volume: nfs: server: nas.lan # NFS server hostname or IP path: /export/kopia # the export on the server (must be an absolute path) encryption: passwordSecretRef: name: nfs-repo-creds key: KOPIA_PASSWORD create: enabled: trueThis is the lowest-friction path to an on-prem NAS repository. No PVC, no StorageClass, no provisioner. Just a server and an absolute path. The same volume.nfs shape works on a ClusterRepository.
To back up an NFS export as a source, rather than as the repository, see Example 10.
As a ClusterRepository
Section titled “As a ClusterRepository”A ClusterRepository may also use a filesystem backend.
For a PVC, the claim must exist, and be ReadWriteMany, in whatever namespace the movers run in. See Movers.
An inline NFS export is reachable from any mover namespace, because it is named rather than claimed. That can make it simpler than a cross-namespace PVC, though a cloud or object backend is usually the better fit for a shared platform repository.
Try it end-to-end
Section titled “Try it end-to-end”Prove this backend really takes a backup. The same example file carries a tiny smoke-test: a throwaway PVC, a SnapshotPolicy, and a Snapshot, all pointed at the nas-primary repository above. It takes you from “applied” to “a snapshot on my NAS” in one go.
1. Apply the bundle. That is the backups namespace, the Secret, the repository PVC and Repository, and the smoke-test objects:
$ kubectl apply -f deploy/examples/backends/filesystem.yaml2. Wait for the repository to be Ready. Everything else waits on this. A volume-backed repository bootstraps in a short mover Job, as the note above explains, so this can take a little longer than an object-store repository:
$ kubectl -n backups wait --for=condition=Ready repository/nas-primary --timeout=2mrepository.kopiur.home-operations.com/nas-primary condition met3. Take the smoke backup. The Snapshot uses generateName, so create it rather than apply it. The namespace, Secret, Repository, PVCs, and policy already exist and report unchanged. The Snapshot is the one new object:
$ kubectl create -f deploy/examples/backends/filesystem.yamlsnapshot.kopiur.home-operations.com/smoke-now-abc12 created4. Watch it succeed:
$ kubectl -n backups get snapshots -wNAME PHASE ORIGIN SNAPSHOT AGEsmoke-now-abc12 Pending manual 2ssmoke-now-abc12 Running manual 7ssmoke-now-abc12 Succeeded manual k1f1ec0a8 38sThe output above is illustrative. The Snapshot has no fixed Succeeded condition, so to wait on it in a script, key on the phase:
$ kubectl -n backups wait --for=jsonpath='{.status.phase}'=Succeeded \ snapshot/smoke-now-abc12 --timeout=5m5. Prove the data really moved. status.stats shows non-zero bytesNew and filesNew, and status.snapshot.kopiaSnapshotID is the kopia snapshot ID on your NAS:
$ kubectl -n backups get snapshot smoke-now-abc12 -o jsonpath='{.status.stats}'{"sizeBytes":4096,"bytesNew":1280,"filesNew":2,"filesUnchanged":0}
$ kubectl -n backups get snapshot smoke-now-abc12 -o jsonpath='{.status.snapshot.kopiaSnapshotID}'k1f1ec0a8Both outputs are illustrative; sizes and the ID vary. Non-zero bytesNew proves the backup wrote real content to the repository path.
6. Clean up the smoke-test when you’re done. This leaves the repository PVC in place; delete nas-repo too if you want the repository gone:
$ kubectl -n backups delete snapshot --all # finalizer also deletes the kopia snapshot$ kubectl -n backups delete snapshotpolicy smoke$ kubectl -n backups delete pvc smoke-dataFrom here the rest of the lifecycle is the same on every backend. Only the Repository differs. Put it on a cron with a SnapshotSchedule, described in Backups & schedules and Example 01. Restore by picking a Snapshot, described in Restores and Example 03.
Troubleshooting
Section titled “Troubleshooting”permission deniedon create or connect. Runchown -R 65532 <path>, or set the mover UID to the owner. If the export is owned by a dedicated UID and GID while your apps run as other UIDs, use a shared supplemental group rather thanfsGroup, which NFS ignores. See above.- Mover Job pending. For a PVC, it isn’t bound or isn’t
ReadWriteMany; check the PVC and StorageClass. For NFS, the pod can’t mount the export; confirm theserverandpathare reachable from the cluster nodes and that the export permits them.
See also
Section titled “See also”- Permissions, UID & GID: the ownership story this backend lives and dies by.
- Repositories & backends: the concepts, meaning scope, encryption, and creation.
- Movers, RBAC & credentials: mover Jobs and volume mounts.
- Sibling backend: SFTP, the same NAS reached over SSH instead.