Inspecting & browsing snapshots
List what is in the catalog, then read a snapshot’s files without restoring anything. This is how you answer “is the file I need actually in last night’s backup?” in seconds. All global flags apply.
snapshots list
Section titled “snapshots list”A richer kubectl get snapshots. It shows the policy, the origin, the phase, the kopia snapshot ID, real size and file counts from the run’s stats, and the start time. Rows are sorted newest first, can be filtered, and -A lists across namespaces.
$ kubectl kopiur snapshots list -n mediaNAME POLICY ORIGIN PHASE SNAPSHOT-ID SIZE FILES START AGEnightly-20260611-030012 nightly scheduled Succeeded a1b2c3d4e5f6 5.0 GiB 1000 2026-06-11T03:00:12Z 9hThe filters can be combined:
| Flag | Effect |
|---|---|
--policy NAME |
Only snapshots produced from this SnapshotPolicy. |
--origin scheduled|manual|discovered |
Only this origin. discovered = found in the repository by the catalog scan, not produced by this cluster. |
--repository NAME |
Only snapshots stored in this repository. Matches produced snapshots through their pinned status.resolved.repository and discovered ones through the repository-UID label. |
--repository-kind repository|cluster-repository |
Which kind --repository names (default repository). |
--repository-namespace NS |
Where the --repository lives, when it differs from the query namespace. |
-o wide adds the kopia identity as username@hostname:path, the deletionPolicy, and the pin state Kopiur last observed.
ls / cat / download / browse
Section titled “ls / cat / download / browse”Read a snapshot’s files without restoring anything:
$ kubectl kopiur ls nightly-20260611-030012 -n mediaNAME TYPE SIZE MODIFIEDconfig/ dir 1.2 MiB 2026-06-10 21:14:02movies.db file 4.8 GiB 2026-06-11 02:59:31
$ kubectl kopiur ls nightly-20260611-030012 config -n media$ kubectl kopiur cat nightly-20260611-030012 config/app.yaml -n media$ kubectl kopiur download nightly-20260611-030012 movies.db ./movies.db -n media$ kubectl kopiur browse nightly-20260611-030012 -n media # interactive ls/cd/cat/getAll four take a Snapshot object name, whether scheduled, manual or discovered, so anything snapshots list shows with a kopia snapshot ID. They also take an optional path relative to the snapshot root.
cat streams the file to stdout and is safe for binary data, so you can pipe it anywhere. download writes the file locally, defaulting to the file’s own name, and verifies the byte count against the snapshot manifest; if the count does not match it deletes the partial file rather than leaving a truncated one behind. browse opens a small read-only shell with ls, cd, cat, get, pwd, help and quit.
ls -o wide adds the kopia object ID column, and ls -o json prints the kopia directory manifest exactly as kopia produced it.
The session-pod model
Section titled “The session-pod model”The first command against a repository starts a session pod: a mover Job in the snapshot’s namespace that connects to the repository read-only and then idles.
That first command takes a few seconds, for the pod start and the connect. Every later ls, cat or download against the same repository reuses the warm session and answers instantly.
The session expires on its own after --session-ttl, which defaults to 15m, and the cluster garbage-collects the finished Job a minute later. An abandoned browse therefore cannot hold a repository connection, or a pod, open forever. browse ends its session when you exit, unless you pass --keep.
| Flag | Effect |
|---|---|
--session-ttl DURATION |
How long the session pod stays warm (default 15m). |
--local |
Skip the session pod; read with a local kopia binary (below). |
--kopia-bin PATH |
Which local kopia to run (--local only; default $KOPIUR_KOPIA_BINARY, then kopia on PATH). |
--keep |
(browse only) keep the session warm on exit. |
A ClusterRepository that keeps its credential Secret in another namespace, usually the operator’s, is browsed through a copy the operator writes into the snapshot’s namespace for the session’s lifetime. It does that only under the gates a backup’s projection passes: the repository sets spec.credentialProjection.allowed: true, the run that took the snapshot opted in through its policy’s spec.credentialProjection.enabled: true, and the namespace is in allowedNamespaces. Otherwise browse says which gate is closed, and --local remains the way round it. See the console’s browse docs for the details, which are the same.
There is deliberately no --image flag. The session pod runs the exact mover image the operator’s controller Deployment is configured with, through KOPIUR_MOVER_IMAGE, falling back to the release default. Whatever browses your repository is always what backs it up.
The security model
Section titled “The security model”Read-only is enforced twice, but the two layers are different things.
The first layer is an anti-footgun, not a security boundary. The session connects with kopia’s --readonly, and the CLI can only run a closed, typed set of kopia read commands, so snapshot list and object show. There is no way to express a mutating verb in the client, and the read-only connection refuses writes even if someone execs into the pod by hand.
The second layer is the actual boundary, and it is RBAC. Anyone who can create pods that reference the repository’s credential Secret in that namespace can already read the repository. Browsing needs exactly that power: create the session Job and exec into it. Note that it does not need permission to read Secrets. The pod loads the credentials itself, and they never pass through the user’s hands.
The chart ships an opt-in ClusterRole with exactly the browse permission set. Set rbac.browseRole: true and bind <release>-browse to the humans who should browse; a namespaced RoleBinding scopes them to one namespace. See the RBAC reference.
session end
Section titled “session end”End a warm browse session early. It would expire by TTL anyway.
$ kubectl kopiur session end nightly-20260611-030012 -n media # via a snapshot in that repo$ kubectl kopiur session end --repository nas -n media # or the repository directlysession kopiur-browse-nas-1a2b3c4d ended (Job deleted)The command deletes the session Job, and the session’s whole spec rides on that Job. When no session is open it says so and exits 0, so it is safe to call from cleanup scripts.
Sessions are also labeled kopiur.home-operations.com/session=browse, so a plain kubectl delete job -l kopiur.home-operations.com/session=browse works too.
Try it end-to-end
Section titled “Try it end-to-end”Read a snapshot’s files without restoring anything: list them, cat a config file, download a blob, then poke around interactively.
1. List the snapshots to get a name with a kopia snapshot ID:
$ kubectl kopiur snapshots list -n mediaNAME POLICY ORIGIN PHASE SNAPSHOT-ID SIZE FILES START AGEnightly-manual-20260611030012 nightly manual Succeeded a1b2c3d4e5f6 5.0 MiB 2 2026-06-11T03:00:12Z 1m2. List its files, then read the config file straight to stdout:
$ kubectl kopiur ls nightly-manual-20260611030012 -n mediaNAME TYPE SIZE MODIFIEDconfig/ dir 29 B 2026-06-11 03:00:10movies.db file 5.0 MiB 2026-06-11 03:00:11
$ kubectl kopiur cat nightly-manual-20260611030012 config/app.yaml -n media# demo config the CLI arcs read backname: media-appreplicas: 13. Download the blob locally. It is verified against the snapshot’s byte count:
$ kubectl kopiur download nightly-manual-20260611030012 movies.db ./movies.db -n mediadownloading movies.db to ./movies.db…wrote 5242880 bytes to ./movies.db4. Browse interactively (deep). The shell reuses the warm session, and quit ends it:
$ kubectl kopiur browse nightly-manual-20260611030012 -n mediabrowsing snapshot nightly-manual-20260611030012 (kopia a1b2c3d4e5f6) — read-only; `help` lists commands, `quit` leaveskopiur:/> helpcommands: ls list the current directory cd <dir> enter a directory (cd .. to go up) cat <file> print a file to stdout get <file> [dest] download a file pwd print the current path help this help quit leave (also: exit, q, ctrl-d)kopiur:/> lsNAME TYPE SIZE MODIFIEDconfig/ dir 29 B 2026-06-11 03:00:10movies.db file 5.0 MiB 2026-06-11 03:00:11kopiur:/> cd configkopiur:/config> cat app.yaml# demo config the CLI arcs read backname: media-appreplicas: 1kopiur:/config> get app.yaml ./app.yamlwrote 29 bytes to ./app.yamlkopiur:/config> quit5. Tidy up the warm session early. It would expire by TTL anyway:
$ kubectl kopiur session end nightly-manual-20260611030012 -n mediasession kopiur-browse-nas-1a2b3c4d ended (Job deleted)