Workspaces
Create, start, stop and delete workspaces, the headless remote IDEs that you work in.
Last updated
On this page
- Description and operation
- General
- Workspace types
- Creation parameters
- Hugging Face token
- States
- Lifecycle
- Clone
- Restore
- Quota and limits
- Tasks
- Create a workspace
- Show the status of a workspace
- Start a workspace
- Stop a workspace
- Set the time to live
- Delete a workspace
- List workspaces
- Clone a workspace
- Restore a workspace from a snapshot
- Troubleshooting
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-.
| Part | Description |
|---|---|
| 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. |
| Packages | The toolchain, from nixpkgs. See Packages. |
| Tool server | The interface for programs: files, commands and processes on port 7788. |
| SSH | The interface for terminals and editors. See Connection. |
| Resources | Up 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.
| Aspect | Standalone | Managed |
|---|---|---|
| Created with | POST /v1/workspaces | POST /v1/sessions/{id}/workspaces. See Sessions. |
| Started, stopped and deleted by | You, directly | Its session |
| Reaches an environment | No | The session's environment |
| Deleted | When you delete it | When you delete it in the session, or when the session ends |
session field | Empty | The 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
| Parameter | Required | Description |
|---|---|---|
workspace | Yes | The name. Use team/name for a team workspace. |
quota_gb | Yes | The disk size in GB, 1 to 500. |
repo and branch | No | A repository (owner/name) and a branch to clone into the workspace. Set both. |
packages | No | The packages to install. See Packages. |
env | No | Environment 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_secs | No | The 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
state | Meaning |
|---|---|
creating | Not running yet: waiting for capacity or installing packages. |
ready | Running and reachable. |
stopped | Not running. The home is kept. |
error | The definition is not valid. |
deleted | Being 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
sessionfield 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 takes | A clone does not take |
|---|---|
| The files of the home | The variables |
image, packages and locks | The session. The new workspace is in no session. |
| The disk size | The time to live |
The answer has the field based_on. It names the copy that the clone started from.
| Field | Description |
|---|---|
snapshot | The copy that the clone started from. |
at | The time of that copy. Absent for a copy that this request made. |
age_seconds | The age of the copy in seconds. 0 for a new copy. |
interrupted | true 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.
| Parameter | Required | Description |
|---|---|---|
to | Yes | The name of the new workspace. It follows the rules of a workspace name, and is unique in the team of the source. |
task | No | A 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.
| Parameter | Required | Description |
|---|---|---|
snapshot | Yes | The id of a ready snapshot of a workspace. |
to | Yes | The name of the new workspace. It follows the rules of a workspace name. |
image | No | Replaces the frozen image. |
packages (CLI --package, repeated) | No | Replaces the frozen package list. |
quota_gb | No | Replaces 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.
| Item | Limit |
|---|---|
| Name | 1 to 40 lowercase letters, digits or -. Start with a letter, end with a letter or digit, no --. |
| Reserved names | ws, env, api, admin, system, kl, bench, bld-*, and names that look like an id. |
| Disk | 1 to 500 GB. |
| Packages | 100. |
Tasks
Create a workspace
Reason for the task: Get a new workspace to work in.
Job set-up
- Access: you. A team workspace needs team membership.
- References:
POST /v1/workspaces.
Procedure
Create the workspace. Add
packages,repoandbranchas needed.from kloudlite import Kloudlitekl = Kloudlite()ws = kl.workspaces.create(workspace="backend", quota_gb=20)print(ws["id"], ws["state"])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);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"])require_relative "kloudlite"kl = Kloudlite::Client.newws = kl.workspaces.create(workspace: "backend", quota_gb: 20)puts "#{ws['id']} #{ws['state']}"var kl = new Kloudlite();System.out.println(kl.workspaces().create(new Kloudlite.CreateWorkspace().workspace("backend").quotaGb(20)));kloudlite workspaces create backend --quota-gb 20
Close-out
- Show the status as in task 002. Wait until
stateisready.
Show the status of a workspace
Reason for the task: Check whether a workspace runs, and why not.
Job set-up
- Access: you.
- References:
GET /v1/workspaces/{id}.
Procedure
Show the workspace.
ws = kl.workspaces.get(workspace="backend")print(ws["state"], ws["packages_status"], ws["placed"], ws["degraded"])const ws: any = await kl.workspaces.get({ workspace: "backend" });console.log(ws.state, ws.packages_status, ws.placed, ws.degraded);ws, err := kl.Workspaces.Get("backend")if err != nil { log.Fatal(err)}fmt.Println(ws["state"], ws["packages_status"], ws["placed"], ws["degraded"])ws = kl.workspaces.get(workspace: "backend")puts [ws["state"], ws["packages_status"], ws["placed"], ws["degraded"]].join(" ")System.out.println(kl.workspaces().get("backend"));kloudlite workspaces get backend
Close-out
- Make sure that
stateis what you expect. If not, see Troubleshooting.
Start a workspace
Reason for the task: Run a stopped workspace.
Job set-up
- Access: you.
- References:
POST /v1/workspaces/{id}/start.
Procedure
Start the workspace.
kl.workspaces.start(workspace="backend")await kl.workspaces.start({ workspace: "backend" });if err := kl.Workspaces.Start("backend"); err != nil { log.Fatal(err)}kl.workspaces.start(workspace: "backend")kl.workspaces().start("backend");kloudlite workspaces start backend
Close-out
- Show the status as in task 002. Wait until
stateisready.
Stop a workspace
Reason for the task: Release the compute of a workspace and keep its home.
Job set-up
- Access: you.
- References:
POST /v1/workspaces/{id}/stop.
All processes end and all connections close.
Procedure
Stop the workspace.
kl.workspaces.stop(workspace="backend")await kl.workspaces.stop({ workspace: "backend" });if err := kl.Workspaces.Stop("backend"); err != nil { log.Fatal(err)}kl.workspaces.stop(workspace: "backend")kl.workspaces().stop("backend");kloudlite workspaces stop backend
Close-out
- Show the status as in task 002. Make sure that
stateisstopped.
Set the time to live
Reason for the task: Delete a workspace automatically after a period without use.
Job set-up
- Access: you.
- References:
PUT /v1/workspaces/{id}/ttl.
When the time ends, the workspace and its home are deleted. Take a snapshot of work that you want to keep.
Procedure
Set
ttl_secsfrom 600 to 2592000. Set it to no value (None,null,nil) to clear it.kl.workspaces.set_ttl(workspace="backend", ttl_secs=86400)await kl.workspaces.setTtl({ workspace: "backend", ttlSecs: 86400 });ttl := int64(86400)if _, err := kl.Workspaces.SetTTL("backend", &ttl); err != nil { log.Fatal(err)}kl.workspaces.set_ttl(workspace: "backend", ttl_secs: 86400)kl.workspaces().setTtl("backend", 86400L);kloudlite workspaces set-ttl backend --ttl-secs 86400
Close-out
- Make sure that
expires_atshows 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
- Access: you.
- References:
DELETE /v1/workspaces/{id}, Take a snapshot of a workspace.
The home is deleted. Work that is not in a snapshot or a repository is lost.
Procedure
Delete the workspace.
kl.workspaces.delete(workspace="backend")await kl.workspaces.delete({ workspace: "backend" });if err := kl.Request("DELETE", "/v1/workspaces/backend", kloudlite.RequestOptions{}); err != nil { log.Fatal(err)}kl.workspaces.delete(workspace: "backend")kl.workspaces().delete("backend");kloudlite workspaces delete backend
Close-out
- Make sure that
stateisdeleted.
List workspaces
Reason for the task: Find the workspaces that you can use.
Job set-up
- Access: you.
- References:
GET /v1/workspaces,kloudlite workspaces list.
Procedure
Show the list. Pass a team to list the workspaces of that team.
print(kl.workspaces.list())console.log(await kl.workspaces.list());list, err := kl.Workspaces.List("")if err != nil { log.Fatal(err)}fmt.Println(list)p kl.workspaces.listSystem.out.println(kl.workspaces().list(null));kloudlite workspaces list
Close-out
- 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
- Access: you. A team workspace needs team membership.
- References:
POST /v1/workspaces/{id}/clone.
Procedure
Clone the workspace. Use a free name for
to.new = kl.workspaces.clone(workspace="backend", to="api-experiment", task="try the new parser")print(new["id"], new["state"], new["based_on"])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);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"])created = kl.workspaces.clone(workspace: "backend", to: "api-experiment", task: "try the new parser")puts created.values_at("id", "state", "based_on").join(" ")// 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);kloudlite workspaces clone backend --to api-experiment --task "try the new parser"
Close-out
- Read
based_on. Ifinterruptedistrue, examineage_secondsbefore you use the clone. - Wait until the
stateof the new workspace isready.
Restore a workspace from a snapshot
Reason for the task: Make a new workspace from the state that a snapshot froze.
Job set-up
- Access: you. A team snapshot needs team membership.
- Conditions: you know the
idof areadysnapshot of a workspace. See Show the lineage. - References:
POST /v1/workspaces/restore.
Procedure
Restore the workspace. Use the
idforsnapshot, and a free name forto.new = kl.workspaces.restore(snapshot="ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to="api-restored")print(new["id"], new["state"])const created = (await kl.workspaces.restore({ snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to: "api-restored" })) as Record<string, unknown>;console.log(created.id, created.state);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"])created = kl.workspaces.restore(snapshot: "ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to: "api-restored")puts created.values_at("id", "state").join(" ")// 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);kloudlite workspaces restore --snapshot ws-0a1b2c3d4e5f6a7b-8a7b6c5d --to api-restoredAdd
image,packagesorquota_gbto the call in step 1 to replace a frozen value. In the CLI, add--image,--packageor--quota-gb.new = kl.workspaces.restore(snapshot="ws-0a1b2c3d4e5f6a7b-8a7b6c5d", to="api-restored", packages=["ripgrep", "nodejs@20"], quota_gb=40)print(new["id"], new["state"])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);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"])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(" ")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);kloudlite workspaces restore --snapshot ws-0a1b2c3d4e5f6a7b-8a7b6c5d --to api-restored --package ripgrep --package nodejs@20 --quota-gb 40
Close-out
- Wait until the
stateof the new workspace isready. - Examine
packages_status. Make sure that the packages are installed.
Troubleshooting
| Symptom | Possible cause | Corrective 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 here | The 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 Quota | The 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 session | The 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 repo | repo is set without branch. | Set branch. |
409 with workspace is interrupted: its node is down | The machine that ran the workspace failed. | Wait for it to resume, or clone it. |
422 with task must be at most 200 characters | The note in task of a clone is too long. | Use a shorter note. |
409 with not ready yet: no volume for this workspace | The 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 workspace | A 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 restore | The 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 NoCapacity | The platform has no capacity right now. | Wait. The workspace starts when capacity frees up. |
state stays creating and packages_status is Building | Packages are being installed. | Wait. See Packages. |
state is error | The name, source repository or snapshot is not valid. | Delete the workspace and create it again with valid values. |