Environments
Create, start, stop and delete environments, the running services that workspaces develop against.
Last updated
On this page
- Description and operation
- General
- States
- Lifecycle
- Clone
- Restore
- Quota and limits
- Tasks
- Create an environment
- Show the status of an environment
- Start an environment
- Stop an environment
- Set the time to live
- Delete an environment
- List environments
- Clone an environment
- Restore an environment from a snapshot
- Troubleshooting
Description and operation
General
An environment is a set of services that run together, for example an API, a database and a cache. It gives workspaces the real dependencies of an application, so code is developed against them, not against mocks.
A workspace connects to an environment through a session. It then reaches each service by name, for example postgres://db:5432. An intercept sends a service's traffic to a workspace, so a change runs against the rest of the environment before it is merged.
Each environment has its own isolated network, and one disk shared by its services. It belongs to you or to a team; many sessions can connect to one environment.
| Part | Description |
|---|---|
| Services | The programs of the environment, each from an image. See Services. |
| Disk | Holds the data of services that mount a folder. Its size is quota_gb. It is kept while the environment is stopped. |
| Names | Each service is reachable by its name on the ports that it lists. |
To do these tasks with an AI agent, see Environments and intercepts with an agent.
States
state | Meaning |
|---|---|
creating | Starting services, or waiting for capacity. |
running | Running. service_status shows whether each service is ready. |
stopped | Not running. The disk is kept. |
error | The environment failed. |
deleted | Shown only in the response to delete. |
Lifecycle
- Idle stop. An environment runs while a connected session is awake. After 5 minutes with none awake, it stops and sets
idle_since. Using a connected session starts it again; an environment that you stopped stays stopped. - Time to live. The platform deletes an environment that is unused for
ttl_secs(600 to 2592000). An environment with a connected session, awake or idle, is in use and is never deleted. - Delete. The services and the disk are removed. Connected sessions are disconnected, not ended. Snapshots stay.
- Interruption. If the machine that holds a running environment fails, start and clone return
409until it returns. You can clone a snapshot into a new environment.
Clone
A clone is a new environment that starts with a copy of an environment. A clone is not a snapshot. 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 environment at once.
| Clone | What the copy holds |
|---|---|
| Present state | The live data of all services at the time of the request, the service list and the disk size of the source |
| From a snapshot | The data and the definition that a ready snapshot of the environment froze. See Snapshot types. |
A clone of the present state copies the live data of the source to a new disk. It has the owner and the region of the source. It has no based_on, because the platform makes no snapshot for it. It is refused with 409 while the source is interrupted.
A clone from a snapshot moves the expiry of the snapshot to a full default lifetime from now. With snapshot, the owner is the owner of the snapshot, you, or a team that you belong to.
A clone does not take the variables, the sessions, the intercepts or the time to live of its source. The new environment has no connected session. A clone counts as a new environment against the quota of the owner.
| Parameter | Required | Description |
|---|---|---|
to | Yes | The name of the new environment. Use team/name to give the owner with snapshot. |
snapshot | No | The id of a ready snapshot of this environment. Without it, the clone copies the present state. |
services | No | Only with snapshot. Replaces the frozen service list. An empty list means the list of the snapshot. |
quota_gb | No | Only with snapshot. The disk size. Absent means the size that the snapshot froze. |
ttl_secs | No | API only. The time to live of the new environment, 600 to 2592000 seconds. |
To do these tasks with an AI agent, see Agentic workflows – Volumes.
Restore
A restore puts the same environment back to one of its own snapshots. You name the snapshot by its id, and the snapshot must be ready. A restore moves the expiry of the snapshot to a full default lifetime from now. To make a new environment from a snapshot, clone it with snapshot_id.
The data and the service list return to the snapshot. The disk size becomes the size that the snapshot froze. The platform stops the services, puts back the data and starts the services of the snapshot.
The answer lists what the restore does to each service, by name.
| Field | Description |
|---|---|
rolled_back | Services in both lists. Their data returns to the snapshot. |
added | Services that the snapshot has and the environment does not. |
removed | Services that the environment has and the snapshot does not. |
discarded | Services that you named in discard. Their data is lost. |
A service can keep data that the snapshot does not have. The restore is then refused with 409 and a list lost. To accept the loss, name exactly those services in discard.
| Parameter | Required | Description |
|---|---|---|
snapshot | Yes | The id of a ready snapshot of this environment. |
discard (CLI --discard, repeated) | No | The services whose data you agree to lose. Name exactly the services in lost. |
A restore counts the cpu and memoryGb of the services that it adds against the quota. It is refused when the disk of the owner is already above diskGb. It is also refused while a service that it removes has an intercept.
Quota and limits
An environment counts its disk, and the vCPU and memory of its running services, against the owner's quota.
| 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, bld-*, and names that look like an id. |
| Disk | 1 to 500 GB. The default is 20 GB. |
Tasks
Create an environment
Reason for the task: Run the services of an application for workspaces to develop against.
Job set-up
- Access: you. A team environment needs team membership.
- References:
POST /v1/environments, Services.
Procedure
Create the environment with its services.
from kloudlite import Kloudlitekl = Kloudlite()env = kl.environments.create( environment="acme-dev", services=[{"name": "db", "image": "postgres:16", "ports": [5432], "env": {"POSTGRES_PASSWORD": "dev"}, "mounts": [{"folder": "pgdata", "path": "/var/lib/postgresql/data"}]}], quota_gb=20,)print(env["id"], env["state"])import { Kloudlite } from "./index.ts";const kl = new Kloudlite();const env = (await kl.environments.create({ environment: "acme-dev", services: [{ name: "db", image: "postgres:16", ports: [5432], env: { POSTGRES_PASSWORD: "dev" }, mounts: [{ folder: "pgdata", path: "/var/lib/postgresql/data" }], }], quotaGb: 20,})) as Record<string, unknown>;console.log(env.id, env.state);kl := kloudlite.New(kloudlite.Options{})env, err := kl.Environments.Create(kloudlite.CreateEnvironment{ Environment: "acme-dev", Services: []kloudlite.Service{{ Name: "db", Image: "postgres:16", Ports: []int{5432}, Env: map[string]string{"POSTGRES_PASSWORD": "dev"}, Mounts: []kloudlite.Mount{{Folder: "pgdata", Path: "/var/lib/postgresql/data"}}, }}, QuotaGb: 20,})if err != nil { log.Fatal(err)}fmt.Println(env["id"], env["state"])require_relative "kloudlite"kl = Kloudlite::Client.newenv = kl.environments.create( environment: "acme-dev", services: [{ name: "db", image: "postgres:16", ports: [5432], env: { "POSTGRES_PASSWORD" => "dev" }, mounts: [{ folder: "pgdata", path: "/var/lib/postgresql/data" }] }], quota_gb: 20)puts env.values_at("id", "state").join(" ")var kl = new Kloudlite();System.out.println(kl.environments().create(new Kloudlite.CreateEnvironment() .environment("acme-dev") .services(new Kloudlite.Service() .name("db").image("postgres:16").port(5432) .env("POSTGRES_PASSWORD", "dev") .mount("pgdata", "/var/lib/postgresql/data")) .quotaGb(20)));kloudlite environments create acme-dev --quota-gb 20 --services '[ {"name": "db", "image": "postgres:16", "ports": [5432], "env": {"POSTGRES_PASSWORD": "dev"}, "mounts": [{"folder": "pgdata", "path": "/var/lib/postgresql/data"}]}]'
Close-out
- Wait until
stateisrunningand every service inservice_statusis ready.
Show the status of an environment
Reason for the task: Check whether an environment and its services run, and why not.
Job set-up
- Access: you. A team environment needs team membership.
- References:
GET /v1/environments/{id},GET /v1/environments.
Procedure
Show the environment.
kloudlite environments listlists them all.env = kl.environments.get(environment="acme-dev")print(env["state"], env.get("service_status"), env.get("idle_since"))const env = (await kl.environments.get({ environment: "acme-dev" })) as Record<string, unknown>;console.log(env.state, env.service_status, env.idle_since);env, err := kl.Environments.Get("acme-dev")if err != nil { log.Fatal(err)}fmt.Println(env["state"], env["service_status"], env["idle_since"])env = kl.environments.get(environment: "acme-dev")puts env.values_at("state", "service_status", "idle_since").inspectSystem.out.println(kl.environments().get("acme-dev"));kloudlite environments get acme-dev
Close-out
- Make sure that
stateandservice_statusare what you expect. If not, see Troubleshooting.
Start an environment
Reason for the task: Run a stopped environment.
Job set-up
- Access: you. A team environment needs team membership.
- References:
POST /v1/environments/{id}/start.
Procedure
Start the environment.
kl.environments.start(environment="acme-dev")await kl.environments.start({ environment: "acme-dev" });if err := kl.Environments.Start("acme-dev"); err != nil { log.Fatal(err)}kl.environments.start(environment: "acme-dev")kl.environments().start("acme-dev");kloudlite environments start acme-dev
Close-out
- Wait until
stateisrunningand every service is ready.
Stop an environment
Reason for the task: Release the compute of an environment and keep its data.
Job set-up
- Access: you. A team environment needs team membership.
- References:
POST /v1/environments/{id}/stop.
All services stop. Connected workspaces lose access to them until the environment starts again.
Procedure
Stop the environment.
kl.environments.stop(environment="acme-dev")await kl.environments.stop({ environment: "acme-dev" });if err := kl.Environments.Stop("acme-dev"); err != nil { log.Fatal(err)}kl.environments.stop(environment: "acme-dev")kl.environments().stop("acme-dev");kloudlite environments stop acme-dev
Close-out
- Make sure that
stateisstopped. Awarningin the response means that the machine of the environment is down.
Set the time to live
Reason for the task: Delete an environment automatically after a period without use.
Job set-up
- Access: you. A team environment needs team membership.
- References:
PUT /v1/environments/{id}/ttl.
When the time ends, the services and the disk are deleted. Take a snapshot of data that you want to keep.
Procedure
Set
ttl_secsfrom 600 to 2592000. Set it to no value (None,null,nil) to clear it.kl.environments.set_ttl(environment="acme-dev", ttl_secs=86400)await kl.environments.setTtl({ environment: "acme-dev", ttlSecs: 86400 });ttl := int64(86400)if _, err := kl.Environments.SetTTL("acme-dev", &ttl); err != nil { log.Fatal(err)}kl.environments.set_ttl(environment: "acme-dev", ttl_secs: 86400)kl.environments().setTtl("acme-dev", 86400L);kloudlite environments set-ttl acme-dev --ttl-secs 86400
Close-out
- Make sure that
expires_atshows a time, or no value if you cleared it.
Delete an environment
Reason for the task: Remove an environment that you no longer need.
Job set-up
- Access: you. A team environment needs team membership.
- References:
DELETE /v1/environments/{id}, Volumes.
The services and the disk are deleted. Data that is not in a snapshot is lost.
Procedure
Delete the environment.
kl.environments.delete(environment="acme-dev")await kl.environments.delete({ environment: "acme-dev" });if err := kl.Environments.Delete("acme-dev"); err != nil { log.Fatal(err)}kl.environments.delete(environment: "acme-dev")kl.environments().delete("acme-dev");kloudlite environments delete acme-dev
Close-out
- Make sure that the environment is no longer listed. A
warningin the response names a session that was not disconnected.
List environments
Reason for the task: Find the environments that you and your teams can use.
Job set-up
- Access: you.
- References:
GET /v1/environments,kloudlite environments list.
Procedure
Show the list. Pass an owner to list the environments of one handle or team.
print(kl.environments.list())console.log(await kl.environments.list());list, err := kl.Environments.List("")if err != nil { log.Fatal(err)}fmt.Println(list)p kl.environments.listSystem.out.println(kl.environments().list(null));kloudlite environments list
Close-out
- Make sure that each environment that you expect is listed.
Clone an environment
Reason for the task: Make a new environment from the present state of an environment, or from one of its snapshots.
Job set-up
- Access: you. A team environment needs team membership.
- Conditions: for a clone from a snapshot, you know the
idof areadysnapshot of this environment. See Show the lineage. - References:
POST /v1/environments/{id}/clone.
Procedure
Clone the present state of the environment. Use a plain name for
to.new = kl.environments.clone(environment="acme/dev", to="dev-copy")print(new["id"], new["state"])const created = (await kl.environments.clone({ environment: "acme/dev", to: "dev-copy" })) as Record<string, unknown>;console.log(created.id, created.state);created, err := kl.Environments.Clone(kloudlite.CloneEnvironment{Environment: "acme/dev", To: "dev-copy"})if err != nil { log.Fatal(err)}fmt.Println(created["id"], created["state"])created = kl.environments.clone(environment: "acme/dev", to: "dev-copy")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.environments().clone(new Kloudlite.CloneEnvironment().environment("acme/dev").to("dev-copy"));System.out.println(created);kloudlite environments clone acme/dev --to dev-copyGive the
idof a snapshot insnapshotin place of step 1, to clone that snapshot. Addquota_gbto change the disk size.new = kl.environments.clone(environment="acme/dev", to="dev-before-migration", snapshot="env-1f2e3d4c5b6a7988-8a7b6c5d", quota_gb=40)print(new["id"], new["state"])const created = (await kl.environments.clone({ environment: "acme/dev", to: "dev-before-migration", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", quotaGb: 40,})) as Record<string, unknown>;console.log(created.id, created.state);created, err := kl.Environments.Clone(kloudlite.CloneEnvironment{ Environment: "acme/dev", To: "dev-before-migration", Snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", QuotaGb: 40,})if err != nil { log.Fatal(err)}fmt.Println(created["id"], created["state"])created = kl.environments.clone(environment: "acme/dev", to: "dev-before-migration", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", quota_gb: 40)puts created.values_at("id", "state").join(" ")String created = kl.environments().clone(new Kloudlite.CloneEnvironment() .environment("acme/dev") .to("dev-before-migration") .snapshot("env-1f2e3d4c5b6a7988-8a7b6c5d") .quotaGb(40));System.out.println(created);kloudlite environments clone acme/dev --to dev-before-migration --snapshot env-1f2e3d4c5b6a7988-8a7b6c5d --quota-gb 40
Close-out
- Wait until the
stateof the new environment isrunning. - Examine
service_statusof the new environment. Make sure that each service is ready.
Restore an environment from a snapshot
Reason for the task: Put an environment back to the services and the data of one of its snapshots.
Job set-up
- Access: you. A team environment needs team membership.
- Conditions: you know the
idof areadysnapshot of this environment. No service that the restore removes has an intercept. - References:
POST /v1/environments/{id}/restore, Remove an intercept.
The restore replaces the present data of the environment with the data of the snapshot. Take a snapshot first to keep the present state.
Procedure
Restore the environment. Use the
idof the snapshot forsnapshot.plan = kl.environments.restore(environment="acme/dev", snapshot="env-1f2e3d4c5b6a7988-8a7b6c5d")print(plan)const plan = await kl.environments.restore({ environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d" });console.log(plan);// Environments.Restore keeps only the error, so read the answer with a raw request.var plan anyif err := kl.Request("POST", "/v1/environments/dev/restore", kloudlite.RequestOptions{ Query: url.Values{"owner": {"acme"}}, Body: map[string]any{"snapshot_id": "env-1f2e3d4c5b6a7988-8a7b6c5d", "discard": []string{}}, Out: &plan,}); err != nil { log.Fatal(err)}fmt.Println(plan)plan = kl.environments.restore(environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d")p planString plan = kl.environments().restore(new Kloudlite.RestoreEnvironment().environment("acme/dev").snapshot("env-1f2e3d4c5b6a7988-8a7b6c5d"));System.out.println(plan);kloudlite environments restore acme/dev --snapshot env-1f2e3d4c5b6a7988-8a7b6c5dIf the call returns
409with a listlost, restore again. Name those services indiscard, or repeat--discard SERVICEin the CLI.plan = kl.environments.restore(environment="acme/dev", snapshot="env-1f2e3d4c5b6a7988-8a7b6c5d", discard=["redis"])print(plan)const plan = await kl.environments.restore({ environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", discard: ["redis"] });console.log(plan);err := kl.Environments.Restore(kloudlite.RestoreEnvironment{Environment: "acme/dev", Snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", Discard: []string{"redis"}})if err != nil { log.Fatal(err)}plan = kl.environments.restore(environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", discard: ["redis"])p planString plan = kl.environments().restore(new Kloudlite.RestoreEnvironment().environment("acme/dev").snapshot("env-1f2e3d4c5b6a7988-8a7b6c5d").discard("redis"));System.out.println(plan);kloudlite environments restore acme/dev --snapshot env-1f2e3d4c5b6a7988-8a7b6c5d --discard redis
Close-out
- Read
rolled_back,added,removedanddiscardedin the answer. - Wait until the
stateof the environment isrunning.
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 an environment named "acme-dev" already exists here | The owner already has an environment with this name. | Use another name, or delete the old environment. |
409 with environments: 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 environments, or request more quota. |
404 with not found | The environment does not exist, or you are not in its team. | Check the name. Use team/name for a team environment. |
409 with is ambiguous; qualify it as owner/name | You and a team both have an environment with this name. | Use team/name. |
409 with environment is interrupted: its node is down | The machine that holds the environment failed. | Wait for it to resume, or clone a snapshot into a new environment. |
409 with the source environment is interrupted: its node is down, … | A clone of the present state needs a running machine for the source. | Wait until it returns, or clone a snapshot as in task 008, step 2. |
400 with `owner`, `services` and `quota_gb` apply only with `snapshot_id` | You gave these fields in a clone of the present state. | Remove them, or give a snapshot. |
400 with an environment needs at least one service; this snapshot froze none, so pass `services` | The snapshot froze no services. | Give services in the clone. |
400 with this snapshot was cut from a workspace; use POST /v1/workspaces/restore | You cloned an environment from a workspace snapshot. | Restore a workspace from it. |
404 with not found in a clone or a restore | The snapshot does not exist, is not yours, is not ready, or is not of this environment. | Use an id with the phase ready from the history. |
409 with these services keep data that the snapshot does not have; name them in `discard` to lose it | The restore drops a service or a folder that keeps data. | Read lost. To accept the loss, name those services in discard. |
422 with `discard` names services that lose nothing: redis | discard names a service that loses no data. | Keep only the names in lost. |
422 with snapshot predates saved services; clone it into a new environment instead | The snapshot froze no service list. | Clone the snapshot into a new environment. |
409 with api is intercepted by …; release the intercept before restoring | A service that the restore removes has an intercept. | Remove the intercept, then restore again. |
409 with not ready yet: no volume for this workspace | The environment has no disk yet. | Wait until it is ready, then restore again. |
state stays creating and placed is NoCapacity | The platform has no capacity right now. | Wait, or stop an environment that you do not need. |