Skip to content
DocsAll docs
Console

Lineage

Show the chain of snapshots of a volume, find its tip, and delete a snapshot or a version from the chain.

Last updated

On this page

Overview

The lineage of a volume is the chain of its snapshots. Each snapshot names its parent, the snapshot that it was taken after. The lineage shows the lineage newest first. A snapshot stays in the lineage until it expires or you delete it.

text
backend-base@3 ← backend-base@2 ← backend-base@1      tip (main) = backend-base@3"before the refactor"  "add tests"    "first push"

Sync points are not part of the lineage. A clone makes one from its source. They are never listed, are not a restore target, and cannot be deleted by hand.

PartDescription
idThe name of the snapshot. It starts with the volume id. Restore, clone, and delete take it.
versionThe number of the push under the name of its volume, or null. See Names and versions.
parentThe snapshot that came before this one. null for the first snapshot.
parent_versionThe version of the parent. Only the versions list shows it. null when the parent has no number or is deleted.
messageAn optional note from the push.
createdAtThe time the snapshot was taken.
phaseThe progress of the snapshot. See States.
stateWhat the source was made of at the time. See Snapshot types.
saved_byThe pipeline, run, and job that made the version, or null for a push by hand. Only the versions list shows it.

The lineage lists every snapshot of the volume that you own, in every phase, also error. The versions list shows only snapshots that have a number.

Order and tip

The lineage puts the snapshot with the longest chain of parents first. Snapshots with an equal chain length come newest first. The first row is main, the tip of the lineage. main can be in any phase.

A name reference works on version numbers, not on this order. go-base is the ready version with the highest number. After a branch, main and go-base can name different snapshots.

Deleting a snapshot does not change the parent of the snapshots after it. Their parent still names the deleted id.

Working copies

Add include=working to the history request to see where each working copy stands. The answer is {history, working} instead of the bare array.

Each working entry has kind, name, id, state, base, and head. base is the snapshot the working copy was built from. For a clone, it is the newest push of the source at the time of the clone. head is the latest push of the working copy.

Each history row also has working_copies. It lists the names of the working copies whose head is that row.

Restore and clone

A restore does not change the lineage. It makes a new working copy of the volume at a past snapshot. The first push of that working copy takes the restored snapshot as its parent. The lineage then branches.

text
backend-base@5 ← backend-base@3 ← backend-base@2 ← backend-base@1                                                          ↑                                       backend-base@4 ────┘

Here the workspace backend pushed versions 1, 2, 3, and then 5. A second workspace was restored at version 1 and pushed version 4. The lineage order is 5, 3, 4, 2, 1. A version number counts pushes of the volume, not depth.

A push takes as its parent the newest ready snapshot that the same workspace or environment pushed. When it pushed none, the parent is the snapshot it started from. These rules follow from the push:

  • A workspace made by a restore starts from the snapshot that you named.
  • A workspace or an environment made by a clone starts from the sync point that the clone made. The first push names that sync point as parent. No row of the lineage has that id.
  • An environment that you restore in place keeps its own pushes. Its next push chains on its newest ready snapshot, not on the restored one.
  • An environment made from a snapshot starts from that snapshot. A clone of the live state of an environment uses the volume of its source. It starts from the sync point of the clone.
  • A pipeline job with saveTo adds a version to the volume. saved_by names the pipeline, the run, and the job.

Restore and clone from a ready snapshot move its expiry to a full default lifetime from now. See Expiry.

Delete from the lineage

You delete one snapshot at a time. A delete is immediate. It is refused with 409 for a sync point and for a snapshot that a running workspace or environment stands on.

A pipeline can read a snapshot. A pipeline reads a version when its latest version pins it, as go-base@2, or names its snapshot id. A pipeline that names go-base with no number reads the last ready snapshot of the volume. A queued or running run reads what it started from. The refusal names the readers.

When you delete the last snapshot of a detached volume, the volume is deleted too.

To do these tasks with an AI agent, see Agentic workflows.

Tasks

List the lineage

Reason for the task

Find the snapshots of a volume in order, with their ids and parents.

Job set-up

  • Access: you, or a member of the team that owns the volume.
  • Conditions: you know the volume id. Read it from the volume field of the workspace or the environment, or see List the volumes.
  • References: GET /v1/volumes/{key}/history.

Procedure

  1. Show the snapshots, newest first.

    Python
    for row in kl.volumes.history(volume="ws-0a1b2c3d4e5f6a7b"):    print(row["id"], row["version"], row["parent"], row["createdAt"], row["phase"], row["message"])
    TypeScript
    const rows = (await kl.volumes.history({ volume: "ws-0a1b2c3d4e5f6a7b" })) as Record<string, unknown>[];for (const row of rows) {  console.log(row.id, row.version, row.parent, row.createdAt, row.phase, row.message);}
    Go
    rows, err := kl.Volumes.History("ws-0a1b2c3d4e5f6a7b")if err != nil {    log.Fatal(err)}for _, row := range rows {    fmt.Println(row["id"], row["version"], row["parent"], row["createdAt"], row["phase"], row["message"])}
    Ruby
    kl.volumes.history(volume: "ws-0a1b2c3d4e5f6a7b").each do |row|  puts row.values_at("id", "version", "parent", "createdAt", "phase", "message").join(" ")end
    Java
    // The SDK returns the answer as raw JSON. Read the fields with your own JSON library.System.out.println(kl.volumes().history("ws-0a1b2c3d4e5f6a7b"));
    CLI
    kode snapshots list ws-0a1b2c3d4e5f6a7b

Close-out

  1. Write down the id of the snapshot for a restore, a clone, or a delete.

List the versions of a volume

Reason for the task

Find the version numbers of a named volume, their parents, and which of them a pipeline saved.

Job set-up

Procedure

  1. Show the versions, newest first.

    Python
    for v in kl.volumes.versions(owner="acme", name="go-base"):    print(v["version"], v["snapshot"], v["phase"], v["parent_version"], v["expires_at"], v["saved_by"])
    TypeScript
    const versions = (await kl.volumes.versions({ owner: "acme", name: "go-base" })) as Record<string, unknown>[];for (const v of versions) {  console.log(v.version, v.snapshot, v.phase, v.parent_version, v.expires_at, v.saved_by);}
    Go
    versions, err := kl.Volumes.Versions("acme", "go-base")if err != nil {    log.Fatal(err)}for _, v := range versions {    fmt.Println(v["version"], v["snapshot"], v["phase"], v["parent_version"], v["expires_at"], v["saved_by"])}
    Ruby
    kl.volumes.versions(owner: "acme", name: "go-base").each do |v|  puts v.values_at("version", "snapshot", "phase", "parent_version", "expires_at").join(" ")end
    Java
    // The SDK returns the answer as raw JSON. Read the fields with your own JSON library.System.out.println(kl.volumes().versions("acme", "go-base"));
    CLI
    kode volumes versions acme/go-base

Close-out

  1. Make sure that the newest ready version has the number that you expect. A gap in the numbers is normal.

Show the tip

Reason for the task

Find the snapshot at the head of the lineage.

Job set-up

Procedure

  1. Show the snapshot in latest. The value is null for a volume with no snapshot.

    Python
    print(kl.volumes.refs(volume="ws-0a1b2c3d4e5f6a7b")["latest"])
    TypeScript
    const refs = (await kl.volumes.refs({ volume: "ws-0a1b2c3d4e5f6a7b" })) as Record<string, unknown>;console.log(refs.latest);
    Go
    refs, err := kl.Volumes.Refs("ws-0a1b2c3d4e5f6a7b")if err != nil {    log.Fatal(err)}fmt.Println(refs["latest"])
    Ruby
    puts kl.volumes.refs(volume: "ws-0a1b2c3d4e5f6a7b")["latest"]
    Java
    // The SDK returns the answer as raw JSON. Read the fields with your own JSON library.System.out.println(kl.volumes().refs("ws-0a1b2c3d4e5f6a7b"));
    CLI
    kode snapshots refs ws-0a1b2c3d4e5f6a7b

Close-out

  1. Make sure that the id is the first row of the lineage, as in task 001.

Delete a snapshot from the lineage

Reason for the task

Remove a snapshot that you do not need, and release its quota.

Job set-up

WARNING

The deletion is immediate and final. If it is the last snapshot of a detached volume, the volume and its data are deleted too.

Procedure

  1. Delete the snapshot. Use the id from the lineage for snapshot.

    Python
    kl.volumes.delete_snapshot(volume="ws-0a1b2c3d4e5f6a7b", snapshot="ws-0a1b2c3d4e5f6a7b-8a7b6c5d")
    TypeScript
    await kl.volumes.deleteSnapshot({ volume: "ws-0a1b2c3d4e5f6a7b", snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d" });
    Go
    if err := kl.Volumes.DeleteSnapshot(kloudlite.DeleteSnapshot{Volume: "ws-0a1b2c3d4e5f6a7b", Snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d"}); err != nil {    log.Fatal(err)}
    Ruby
    kl.volumes.delete_snapshot(volume: "ws-0a1b2c3d4e5f6a7b", snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d")
    Java
    kl.volumes().deleteSnapshot(new Kloudlite.DeleteSnapshot().volume("ws-0a1b2c3d4e5f6a7b").snapshot("ws-0a1b2c3d4e5f6a7b-8a7b6c5d"));
    CLI
    kode snapshots delete ws-0a1b2c3d4e5f6a7b ws-0a1b2c3d4e5f6a7b-8a7b6c5d

Close-out

  1. List the lineage, as in task 001. Make sure that the id is gone, or that the call returns 404 because the volume went with it.

Delete a version from the lineage

Reason for the task

Remove one version of a named volume to free disk, and keep the others.

Job set-up

WARNING

The platform deletes the version at once. A job that pins go-base@2 then fails at start.

Procedure

  1. Delete the version.

    Python
    kl.volumes.delete_version(owner="acme", name="go-base", n=2)
    TypeScript
    await kl.volumes.deleteVersion({ owner: "acme", name: "go-base", n: 2 });
    Go
    if err := kl.Volumes.DeleteVersion("acme", "go-base", 2); err != nil {    log.Fatal(err)}
    Ruby
    kl.volumes.delete_version(owner: "acme", name: "go-base", n: 2)
    Java
    kl.volumes().deleteVersion("acme", "go-base", 2);
    CLI
    kode volumes delete-version acme/go-base 2

Close-out

  1. Show the versions as in task 002. Make sure that the number is gone.

Troubleshooting

SymptomPossible causeCorrective action
404 with not foundThe volume or the snapshot does not exist, is not yours, or expired.List the lineage and use an id from the list.
404 with volume go-base has no version 9The version does not exist or is deleted.Use a number from the versions.
The lineage does not show a snapshot that you expectThe snapshot is a sync point, or another owner pushed it.Use an id of a snapshot that you own.
main is not the snapshot with the highest versionThe lineage branched after a restore.Use the version number to select a version. See Order and tip.
409 with a sync point cannot be deleted by handThe id is a sync point, not a snapshot.Use an id from the lineage.
409 with version 2 of go-base is used by pipeline buildA pipeline or a run reads the version.Change the pipeline, or wait for the run. See Readers.
409 with this snapshot is the base of {kind} {display}A workspace or environment was built from this snapshot.Delete that workspace or environment, or delete a different snapshot.
409 with this snapshot is where {kind} {display} stands now (its latest push)The snapshot is the latest push of a workspace or environment.Push again, stop it, or delete a different snapshot.
503 with the cluster could not be reachedThe platform cannot check what uses the snapshot. Nothing is deleted.Wait, then delete again.