Skip to content
DocsAll docs
Console

Workspaces

Create, start, stop and delete workspaces, the headless remote IDEs that you work in.

Last updated

On this page

Description and operation

General

A workspace is a headless remote IDE. It has the parts of an IDE without the window: a file system, a toolchain, a terminal, processes and git. You open the same workspace from your terminal or editor over SSH.

Each workspace has its own kernel, file system and network stack, with allocated vCPU, memory and disk. A workspace belongs to you or to a team. Its name is unique for that owner, and the platform also gives it an id that starts with ws-.

PartDescription
Home/home/kl. The disk of the workspace. It holds all files, caches and settings, and it is kept while the workspace is stopped.
Source directory~/workspace. Editors open this directory.
PackagesThe toolchain, from nixpkgs. See Packages.
Tool serverThe interface for programs: files, commands and processes on port 7788.
SSHThe interface for terminals and editors. See Connection.
ResourcesUp to 4 vCPU and 8 GB of memory. The disk size is quota_gb.

To do these tasks with an AI agent, see Workspaces with an agent.

Workspace types

A workspace is standalone or managed. The type is set at creation and does not change.

AspectStandaloneManaged
Created withPOST /v1/workspacesPOST /v1/sessions/{id}/workspaces. See Sessions.
Started, stopped and deleted byYou, directlyIts session
Reaches an environmentNoThe session's environment
DeletedWhen you delete itWhen you delete it in the session, or when the session ends
session fieldEmptyThe session's name

A standalone workspace cannot join a session later. To work with an environment, create a managed workspace in a session.

Creation parameters

ParameterRequiredDescription
workspaceYesThe name. Use team/name for a team workspace.
quota_gbYesThe disk size in GB, 1 to 500.
repo and branchNoA repository (owner/name) and a branch to clone into the workspace. Set both.
packagesNoThe packages to install. See Packages.
envNoEnvironment variables. A value of ${{ secrets.NAME }} refers to a secret. In a team workspace, your personal secret in the team comes first, then the secret of the team. See Env Vars.
ttl_secsNoThe time to live in seconds. See Lifecycle.

Hugging Face token

A workspace that starts after a Hugging Face connect has the environment variable HF_TOKEN. The SSH sessions of the workspace have it too. Your connector comes before the connector of the team. If the env of the workspace names HF_TOKEN, your value is used.

A running process keeps the token that it started with. A refresh of the token restarts nothing.

States

stateMeaning
creatingNot running yet: waiting for capacity or installing packages.
readyRunning and reachable.
stoppedNot running. The home is kept.
errorThe definition is not valid.
deletedBeing deleted.

The status also has packages_status (installation progress), placed (NoCapacity while the platform has no room), degraded (NodeDead while interrupted), idle_since and expires_at.

Lifecycle

  • In a session. A workspace in a session is controlled through its session. Starting or stopping the session starts or stops it, and ending the session deletes it. Its session field names the session.
  • Stop. All processes end and the home is kept. Detached processes started through the tool server start again with the workspace.
  • Idle stop. A workspace in a session stops when the session is idle. Any other workspace stops after 30 minutes without use. An SSH connection or a tool server request counts as use. Using an idle-stopped workspace starts it again; a workspace that you stopped stays stopped.
  • Interruption. If the machine that runs a workspace fails, start returns an error until the machine returns. You can clone the workspace from its last saved state.
  • Time to live. The platform deletes a workspace that is unused for ttl_secs (600 to 2592000). A running workspace, or one whose session is awake, is never deleted.
  • Delete. The workspace and its home are removed. Snapshots stay.

Clone

A clone is a new workspace that starts with a copy of the live state of a workspace. The platform makes the copy at the time of the request, also while the source runs. A clone is not a snapshot, and it does not change the snapshot history.

The copy is independent: changes in the clone do not change the source, and changes in the source do not change the clone. The clone call answers 202 and starts the new workspace at once. The new workspace has the team and the region of its source, and it uses the same volume.

A clone takesA clone does not take
The files of the homeThe variables
image, packages and locksThe session. The new workspace is in no session.
The disk sizeThe time to live

The answer has the field based_on. It names the copy that the clone started from.

FieldDescription
snapshotThe copy that the clone started from.
atThe time of that copy. Absent for a copy that this request made.
age_secondsThe age of the copy in seconds. 0 for a new copy.
interruptedtrue if the source was interrupted, and the clone started from an older copy.

If the machine of the source failed, the platform cannot make a new copy. The clone then starts from the newest copy that another machine holds, on a volume of its own. interrupted is true, and age_seconds shows how old the files are. If no other machine holds a copy, the clone is refused.

ParameterRequiredDescription
toYesThe name of the new workspace. It follows the rules of a workspace name, and is unique in the team of the source.
taskNoA note on what the copy is for. Up to 200 characters.

A clone counts as a new workspace against the quota of the owner.

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

Restore

A restore makes a new workspace from a snapshot of a workspace. You name the snapshot by a reference: a volume name, name@N or the id of a snapshot. The snapshot must be ready. To restore a volume of a team, give the team. A volume of another owner is never restored: the call answers 404. A restore moves the expiry of the snapshot to a full default lifetime from now.

The new workspace is on the volume of the snapshot. The source workspace can be deleted. A restore then attaches the detached volume again.

The new workspace takes the definition that the snapshot froze: image, packages, locks, resources and quotaGb. A value in the request replaces the frozen value. An unchanged package keeps its lock, and a new pinned package is resolved.

The new workspace belongs to you. It is in the team of the source while the source exists, else it is personal. It is in no session, has no variables and no time to live.

ParameterRequiredDescription
snapshotYesThe id of a ready snapshot of a workspace.
toYesThe name of the new workspace. It follows the rules of a workspace name.
imageNoReplaces the frozen image.
packages (CLI --package, repeated)NoReplaces the frozen package list.
quota_gbNoReplaces the frozen disk size. The region sets the maximum.

A restore counts as a new workspace against the quota of its owner. It is refused when the disk of the owner is already above diskGb.

Quota and limits

A running workspace counts its disk, 4 vCPU and 8 GB of memory against the owner's quota. A stopped workspace counts only its disk.

ItemLimit
Name1 to 40 lowercase letters, digits or -. Start with a letter, end with a letter or digit, no --.
Reserved namesws, env, api, admin, system, kl, bench, bld-*, and names that look like an id.
Disk1 to 500 GB.
Packages100.

Tasks

Create a workspace

Reason for the task: Get a new workspace to work in.

Job set-up

Procedure

  1. Create the workspace. Add packages, repo and branch as needed.

    Python
    from kloudlite import Kloudlitekl = Kloudlite()ws = kl.workspaces.create(workspace="backend", quota_gb=20)print(ws["id"], ws["state"])
    TypeScript
    import { Kloudlite } from "./index.ts";const kl = new Kloudlite();const ws: any = await kl.workspaces.create({ workspace: "backend", quotaGb: 20 });console.log(ws.id, ws.state);
    Go
    kl := kloudlite.New(kloudlite.Options{})ws, err := kl.Workspaces.Create(kloudlite.CreateWorkspace{Workspace: "backend", QuotaGb: 20})if err != nil {    log.Fatal(err)}fmt.Println(ws["id"], ws["state"])
    Ruby
    require_relative "kloudlite"kl = Kloudlite::Client.newws = kl.workspaces.create(workspace: "backend", quota_gb: 20)puts "#{ws['id']} #{ws['state']}"
    Java
    var kl = new Kloudlite();System.out.println(kl.workspaces().create(new Kloudlite.CreateWorkspace().workspace("backend").quotaGb(20)));
    CLI
    kloudlite workspaces create backend --quota-gb 20

Close-out

  1. Show the status as in task 002. Wait until state is ready.

Show the status of a workspace

Reason for the task: Check whether a workspace runs, and why not.

Job set-up

Procedure

  1. Show the workspace.

    Python
    ws = kl.workspaces.get(workspace="backend")print(ws["state"], ws["packages_status"], ws["placed"], ws["degraded"])
    TypeScript
    const ws: any = await kl.workspaces.get({ workspace: "backend" });console.log(ws.state, ws.packages_status, ws.placed, ws.degraded);
    Go
    ws, err := kl.Workspaces.Get("backend")if err != nil {    log.Fatal(err)}fmt.Println(ws["state"], ws["packages_status"], ws["placed"], ws["degraded"])
    Ruby
    ws = kl.workspaces.get(workspace: "backend")puts [ws["state"], ws["packages_status"], ws["placed"], ws["degraded"]].join(" ")
    Java
    System.out.println(kl.workspaces().get("backend"));
    CLI
    kloudlite workspaces get backend

Close-out

  1. Make sure that state is what you expect. If not, see Troubleshooting.

Start a workspace

Reason for the task: Run a stopped workspace.

Job set-up

Procedure

  1. Start the workspace.

    Python
    kl.workspaces.start(workspace="backend")
    TypeScript
    await kl.workspaces.start({ workspace: "backend" });
    Go
    if err := kl.Workspaces.Start("backend"); err != nil {    log.Fatal(err)}
    Ruby
    kl.workspaces.start(workspace: "backend")
    Java
    kl.workspaces().start("backend");
    CLI
    kloudlite workspaces start backend

Close-out

  1. Show the status as in task 002. Wait until state is ready.

Stop a workspace

Reason for the task: Release the compute of a workspace and keep its home.

Job set-up

CAUTION

All processes end and all connections close.

Procedure

  1. Stop the workspace.

    Python
    kl.workspaces.stop(workspace="backend")
    TypeScript
    await kl.workspaces.stop({ workspace: "backend" });
    Go
    if err := kl.Workspaces.Stop("backend"); err != nil {    log.Fatal(err)}
    Ruby
    kl.workspaces.stop(workspace: "backend")
    Java
    kl.workspaces().stop("backend");
    CLI
    kloudlite workspaces stop backend

Close-out

  1. Show the status as in task 002. Make sure that state is stopped.

Set the time to live

Reason for the task: Delete a workspace automatically after a period without use.

Job set-up

WARNING

When the time ends, the workspace and its home are deleted. Take a snapshot of work that you want to keep.

Procedure

  1. Set ttl_secs from 600 to 2592000. Set it to no value (None, null, nil) to clear it.

    Python
    kl.workspaces.set_ttl(workspace="backend", ttl_secs=86400)
    TypeScript
    await kl.workspaces.setTtl({ workspace: "backend", ttlSecs: 86400 });
    Go
    ttl := int64(86400)if _, err := kl.Workspaces.SetTTL("backend", &ttl); err != nil {    log.Fatal(err)}
    Ruby
    kl.workspaces.set_ttl(workspace: "backend", ttl_secs: 86400)
    Java
    kl.workspaces().setTtl("backend", 86400L);
    CLI
    kloudlite workspaces set-ttl backend --ttl-secs 86400

Close-out

  1. Make sure that expires_at shows a time, or no value if you cleared it.

Delete a workspace

Reason for the task: Remove a workspace that you no longer need.

Job set-up

WARNING

The home is deleted. Work that is not in a snapshot or a repository is lost.

Procedure

  1. Delete the workspace.

    Python
    kl.workspaces.delete(workspace="backend")
    TypeScript
    await kl.workspaces.delete({ workspace: "backend" });
    Go
    if err := kl.Request("DELETE", "/v1/workspaces/backend", kloudlite.RequestOptions{}); err != nil {    log.Fatal(err)}
    Ruby
    kl.workspaces.delete(workspace: "backend")
    Java
    kl.workspaces().delete("backend");
    CLI
    kloudlite workspaces delete backend

Close-out

  1. Make sure that state is deleted.

List workspaces

Reason for the task: Find the workspaces that you can use.

Job set-up

Procedure

  1. Show the list. Pass a team to list the workspaces of that team.

    Python
    print(kl.workspaces.list())
    TypeScript
    console.log(await kl.workspaces.list());
    Go
    list, err := kl.Workspaces.List("")if err != nil {    log.Fatal(err)}fmt.Println(list)
    Ruby
    p kl.workspaces.list
    Java
    System.out.println(kl.workspaces().list(null));
    CLI
    kloudlite workspaces list

Close-out

  1. Make sure that each workspace that you expect is listed.

Clone a workspace

Reason for the task: Make a new workspace that starts with a copy of the files of a workspace.

Job set-up

Procedure

  1. Clone the workspace. Use a free name for to.

    Python
    new = kl.workspaces.clone(workspace="backend", to="api-experiment", task="try the new parser")print(new["id"], new["state"], new["based_on"])
    TypeScript
    const created = (await kl.workspaces.clone({ workspace: "backend", to: "api-experiment", task: "try the new parser" })) as Record<string, unknown>;console.log(created.id, created.state, created.based_on);
    Go
    created, err := kl.Workspaces.Clone(kloudlite.CloneWorkspace{Workspace: "backend", To: "api-experiment", Task: "try the new parser"})if err != nil {    log.Fatal(err)}fmt.Println(created["id"], created["state"], created["based_on"])
    Ruby
    created = kl.workspaces.clone(workspace: "backend", to: "api-experiment", task: "try the new parser")puts created.values_at("id", "state", "based_on").join(" ")
    Java
    // The SDK returns the answer as raw JSON. Read the fields with your own JSON library.String created = kl.workspaces().clone(new Kloudlite.CloneWorkspace().workspace("backend").to("api-experiment").task("try the new parser"));System.out.println(created);
    CLI
    kloudlite workspaces clone backend --to api-experiment --task "try the new parser"

Close-out

  1. Read based_on. If interrupted is true, examine age_seconds before you use the clone.
  2. Wait until the state of the new workspace is ready.

Restore a workspace from a snapshot

Reason for the task: Make a new workspace from the state that a snapshot froze.

Job set-up

Procedure

  1. Restore the workspace. Use the id for snapshot, and a free name for to.

    Python
    new = kl.workspaces.restore(snapshot="ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to="api-restored")print(new["id"], new["state"])
    TypeScript
    const created = (await kl.workspaces.restore({ snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to: "api-restored" })) as Record<string, unknown>;console.log(created.id, created.state);
    Go
    created, err := kl.Workspaces.Restore(kloudlite.RestoreWorkspace{Snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d", To: "api-restored"})if err != nil {    log.Fatal(err)}fmt.Println(created["id"], created["state"])
    Ruby
    created = kl.workspaces.restore(snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to: "api-restored")puts created.values_at("id", "state").join(" ")
    Java
    // The SDK returns the answer as raw JSON. Read the fields with your own JSON library.String created = kl.workspaces().restore(new Kloudlite.RestoreWorkspace().snapshot("ws-0a1b2c3d4e5f6a7b-8a7b6c5d").to("api-restored"));System.out.println(created);
    CLI
    kloudlite workspaces restore --snapshot ws-0a1b2c3d4e5f6a7b-8a7b6c5d --to api-restored
  2. Add image, packages or quota_gb to the call in step 1 to replace a frozen value. In the CLI, add --image, --package or --quota-gb.

    Python
    new = kl.workspaces.restore(snapshot="ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to="api-restored", packages=["ripgrep", "nodejs@20"], quota_gb=40)print(new["id"], new["state"])
    TypeScript
    const created = (await kl.workspaces.restore({  snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d",  to: "api-restored",  packages: ["ripgrep", "nodejs@20"],  quotaGb: 40,})) as Record<string, unknown>;console.log(created.id, created.state);
    Go
    created, err := kl.Workspaces.Restore(kloudlite.RestoreWorkspace{    Snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d",    To:       "api-restored",    Packages: []string{"ripgrep", "nodejs@20"},    QuotaGb:  40,})if err != nil {    log.Fatal(err)}fmt.Println(created["id"], created["state"])
    Ruby
    created = kl.workspaces.restore(snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to: "api-restored", packages: ["ripgrep", "nodejs@20"], quota_gb: 40)puts created.values_at("id", "state").join(" ")
    Java
    String created = kl.workspaces().restore(new Kloudlite.RestoreWorkspace()    .snapshot("ws-0a1b2c3d4e5f6a7b-8a7b6c5d")    .to("api-restored")    .packages("ripgrep", "nodejs@20")    .quotaGb(40));System.out.println(created);
    CLI
    kloudlite workspaces restore --snapshot ws-0a1b2c3d4e5f6a7b-8a7b6c5d --to api-restored --package ripgrep --package nodejs@20 --quota-gb 40

Close-out

  1. Wait until the state of the new workspace is ready.
  2. Examine packages_status. Make sure that the packages are installed.

Troubleshooting

SymptomPossible causeCorrective action
422 with name must be 1-40 lowercase letters…The name breaks the naming rules.Use a name that follows Quota and limits.
409 with a workspace named "backend" already exists hereThe owner already has a workspace with this name.Use another name, or delete the old workspace.
409 with workspaces: 5 of 5 in use; request more under QuotaThe owner's quota is used up. The first word names the dimension, for example diskGb or cpu.Delete unused workspaces or snapshots, or request more quota.
409 with backend is managed by session dev; use the sessionThe workspace belongs to a session, and the call starts, stops or deletes it directly.Control it through its session.
400 with branch is required with reporepo is set without branch.Set branch.
409 with workspace is interrupted: its node is downThe machine that ran the workspace failed.Wait for it to resume, or clone it.
422 with task must be at most 200 charactersThe note in task of a clone is too long.Use a shorter note.
409 with not ready yet: no volume for this workspaceThe source of a clone has no disk yet.Wait until the source is ready, then clone again.
409 with a snapshot is already being cut for this workspaceA snapshot or a clone of the source is in progress.Wait a short time, then clone again.
409 with the source's node is down and no other node holds a sync point of it yet; …The machine of the source workspace failed, and no other machine holds a copy.Wait until the source returns, or restore a snapshot.
404 with not found in a restoreThe snapshot does not exist, is not yours, or is not ready.Use an id with the phase ready from the history.
400 with this snapshot was cut from an environment; use POST /v1/environments/{id}/clone …You restored a workspace from an environment snapshot.Clone the environment from the snapshot.
state stays creating and placed is NoCapacityThe platform has no capacity right now.Wait. The workspace starts when capacity frees up.
state stays creating and packages_status is BuildingPackages are being installed.Wait. See Packages.
state is errorThe name, source repository or snapshot is not valid.Delete the workspace and create it again with valid values.