Skip to content
DocsAll docs
Console

Environments

Create, start, stop, and delete environments, the running services that workspaces develop against.

Last updated

On this page

Overview

An environment is your app's services, shared by many desks. The services 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 desk. 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 desks can connect to one environment.

PartDescription
ServicesThe programs of the environment, each from an image. See Services.
DiskHolds the data of services that mount a folder. It is kept while the environment is stopped.
NamesEach service is reachable by its name on the ports that it lists.

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

States

stateMeaning
creatingStarting services, or waiting for capacity.
runningRunning. service_status shows whether each service is ready.
stoppedNot running. The disk is kept.
errorThe environment failed.
deletedShown only in the response to delete.

Lifecycle

  • Idle stop. An environment runs while a connected desk is awake. After 5 minutes with none awake, it stops and sets idle_since. Using a connected desk 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 desk, awake or idle, is in use and is never deleted.
  • Delete. The services and the disk are removed. Connected desks are disconnected, not ended. Snapshots stay.
  • Machine loss. If the machine that runs an environment fails or is reclaimed, the platform starts the services on another machine. Processes and memory are lost. Data that the services synced to disk is kept.
  • Storage failure. If a storage machine fails, the storage serves the volume from its second copy and the services keep running.

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.

CloneWhat the copy holds
Present stateThe live data of all services at the time of the request, and the service list
From a snapshotThe data and the definition that a ready snapshot of the environment froze. See Snapshot types.

A clone of the present state makes a copy of the source in seconds. It has the owner and the region of the source. The answer has based_on: snapshot names the copy, age_seconds is always 0, and interrupted is always false.

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 desks, the intercepts, or the time to live of its source. The new environment has no connected desk. A clone counts as a new environment against the quota of the owner.

ParameterRequiredDescription
toYesThe name of the new environment. Use team/name to give the owner with snapshot.
snapshotNoThe id of a ready snapshot of this environment. Without it, the clone copies the present state.
servicesNoOnly with snapshot. Replaces the frozen service list. An empty list means the list of the snapshot.
ttl_secsNoAPI only. The time to live of the new environment, 600 to 2592000 seconds.

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

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 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.

FieldDescription
rolled_backServices in both lists. Their data returns to the snapshot.
addedServices that the snapshot has and the environment does not.
removedServices that the environment has and the snapshot does not.
discardedServices 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. The check looks only at services and their folders. A service in rolled_back is never in lost: its rows written after the snapshot are gone without a 409.

ParameterRequiredDescription
snapshotYesThe id of a ready snapshot of this environment.
discard (CLI --discard, repeated)NoThe 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.

Databases

A database in a service writes to the disk of the environment. The platform keeps every write that the database synced to disk, also when a machine fails. A write that stays in memory is lost.

A database restarts as after a power cut. It replays its own log. A snapshot and a clone hold the same state that a power cut leaves.

A SQLite database needs a safe setting to keep its last commit when a machine fails abruptly. Use PRAGMA synchronous=EXTRA, or use WAL mode with PRAGMA journal_mode=WAL. The setting synchronous=FULL in rollback-journal mode lost the last acknowledged commit in 2 of 7 abrupt kills. The setting synchronous=EXTRA lost it in 0 of 6.

Quota and limits

An environment counts its disk, and the vCPU and memory of its running services, against the owner's quota.

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, kd, bld-*, and names that look like an id.

Tasks

Create an environment

Reason for the task

Run the services of an application for workspaces to develop against.

Job set-up

Procedure

  1. Create the environment with its services.

    Python
    from kloudlite import Kodekl = 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"}]}],)print(env["id"], env["state"])
    TypeScript
    import { Kode } 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" }],  }],})) as Record<string, unknown>;console.log(env.id, env.state);
    Go
    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"])
    Ruby
    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" }] }],)puts env.values_at("id", "state").join(" ")
    Java
    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"))    ));
    CLI
    kode environments create acme-dev --services '[  {"name": "db", "image": "postgres:16", "ports": [5432], "env": {"POSTGRES_PASSWORD": "dev"}, "mounts": [{"folder": "pgdata", "path": "/var/lib/postgresql/data"}]}]'

Close-out

  1. Wait until state is running and every service in service_status is 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

Procedure

  1. Show the environment. kode environments list lists them all.

    Python
    env = kl.environments.get(environment="acme-dev")print(env["state"], env.get("service_status"), env.get("idle_since"))
    TypeScript
    const env = (await kl.environments.get({ environment: "acme-dev" })) as Record<string, unknown>;console.log(env.state, env.service_status, env.idle_since);
    Go
    env, err := kl.Environments.Get("acme-dev")if err != nil {    log.Fatal(err)}fmt.Println(env["state"], env["service_status"], env["idle_since"])
    Ruby
    env = kl.environments.get(environment: "acme-dev")puts env.values_at("state", "service_status", "idle_since").inspect
    Java
    System.out.println(kl.environments().get("acme-dev"));
    CLI
    kode environments get acme-dev

Close-out

  1. Make sure that state and service_status are what you expect. If not, see Troubleshooting.

Start an environment

Reason for the task

Run a stopped environment.

Job set-up

Procedure

  1. Start the environment.

    Python
    kl.environments.start(environment="acme-dev")
    TypeScript
    await kl.environments.start({ environment: "acme-dev" });
    Go
    if err := kl.Environments.Start("acme-dev"); err != nil {    log.Fatal(err)}
    Ruby
    kl.environments.start(environment: "acme-dev")
    Java
    kl.environments().start("acme-dev");
    CLI
    kode environments start acme-dev

Close-out

  1. Wait until state is running and every service is ready.

Stop an environment

Reason for the task

Release the compute of an environment and keep its data.

Job set-up

CAUTION

All services stop. Connected workspaces lose access to them until the environment starts again.

Procedure

  1. Stop the environment.

    Python
    kl.environments.stop(environment="acme-dev")
    TypeScript
    await kl.environments.stop({ environment: "acme-dev" });
    Go
    if err := kl.Environments.Stop("acme-dev"); err != nil {    log.Fatal(err)}
    Ruby
    kl.environments.stop(environment: "acme-dev")
    Java
    kl.environments().stop("acme-dev");
    CLI
    kode environments stop acme-dev

Close-out

  1. Make sure that state is stopped.

Set the time to live

Reason for the task

Delete an environment automatically after a period without use.

Job set-up

WARNING

When the time ends, the services and the disk are deleted. Take a snapshot of data 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.environments.set_ttl(environment="acme-dev", ttl_secs=86400)
    TypeScript
    await kl.environments.setTtl({ environment: "acme-dev", ttlSecs: 86400 });
    Go
    ttl := int64(86400)if _, err := kl.Environments.SetTTL("acme-dev", &ttl); err != nil {    log.Fatal(err)}
    Ruby
    kl.environments.set_ttl(environment: "acme-dev", ttl_secs: 86400)
    Java
    kl.environments().setTtl("acme-dev", 86400L);
    CLI
    kode environments set-ttl acme-dev --ttl-secs 86400

Close-out

  1. Make sure that expires_at shows 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

WARNING

The services and the disk are deleted. Data that is not in a snapshot is lost.

Procedure

  1. Delete the environment.

    Python
    kl.environments.delete(environment="acme-dev")
    TypeScript
    await kl.environments.delete({ environment: "acme-dev" });
    Go
    if err := kl.Environments.Delete("acme-dev"); err != nil {    log.Fatal(err)}
    Ruby
    kl.environments.delete(environment: "acme-dev")
    Java
    kl.environments().delete("acme-dev");
    CLI
    kode environments delete acme-dev

Close-out

  1. Make sure that the environment is no longer listed. A warning in the response names a desk that was not disconnected.

List environments

Reason for the task

Find the environments that you and your teams can use.

Job set-up

Procedure

  1. Show the list. Pass an owner to list the environments of one handle or team.

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

Close-out

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

Clone an environment

Reason for the task

Create a new environment from the present state of an environment, or from one of its snapshots.

Job set-up

Procedure

  1. Clone the present state of the environment. Use a plain name for to.

    Python
    new = kl.environments.clone(environment="acme/dev", to="dev-copy")print(new["id"], new["state"])
    TypeScript
    const created = (await kl.environments.clone({ environment: "acme/dev", to: "dev-copy" })) as Record<string, unknown>;console.log(created.id, created.state);
    Go
    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"])
    Ruby
    created = kl.environments.clone(environment: "acme/dev", to: "dev-copy")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.environments().clone(new Kloudlite.CloneEnvironment().environment("acme/dev").to("dev-copy"));System.out.println(created);
    CLI
    kode environments clone acme/dev --to dev-copy
  2. Give the id of a snapshot in snapshot in place of step 1, to clone that snapshot.

    Python
    new = kl.environments.clone(environment="acme/dev", to="dev-before-migration", snapshot="env-1f2e3d4c5b6a7988-8a7b6c5d")print(new["id"], new["state"])
    TypeScript
    const created = (await kl.environments.clone({  environment: "acme/dev",  to: "dev-before-migration",  snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d",})) as Record<string, unknown>;console.log(created.id, created.state);
    Go
    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"])
    Ruby
    created = kl.environments.clone(environment: "acme/dev", to: "dev-before-migration", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d")puts created.values_at("id", "state").join(" ")
    Java
    String created = kl.environments().clone(new Kloudlite.CloneEnvironment()    .environment("acme/dev")    .to("dev-before-migration")    .snapshot("env-1f2e3d4c5b6a7988-8a7b6c5d")    );System.out.println(created);
    CLI
    kode environments clone acme/dev --to dev-before-migration --snapshot env-1f2e3d4c5b6a7988-8a7b6c5d

Close-out

  1. Wait until the state of the new environment is running.
  2. Examine service_status of 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

WARNING

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

  1. Restore the environment. Use the id of the snapshot for snapshot.

    Python
    plan = kl.environments.restore(environment="acme/dev", snapshot="env-1f2e3d4c5b6a7988-8a7b6c5d")print(plan)
    TypeScript
    const plan = await kl.environments.restore({ environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d" });console.log(plan);
    Go
    // 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)
    Ruby
    plan = kl.environments.restore(environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d")p plan
    Java
    String plan = kl.environments().restore(new Kloudlite.RestoreEnvironment().environment("acme/dev").snapshot("env-1f2e3d4c5b6a7988-8a7b6c5d"));System.out.println(plan);
    CLI
    kode environments restore acme/dev --snapshot env-1f2e3d4c5b6a7988-8a7b6c5d
  2. If the call returns 409 with a list lost, restore again. Name those services in discard, or repeat --discard SERVICE in the CLI.

    Python
    plan = kl.environments.restore(environment="acme/dev", snapshot="env-1f2e3d4c5b6a7988-8a7b6c5d", discard=["redis"])print(plan)
    TypeScript
    const plan = await kl.environments.restore({ environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", discard: ["redis"] });console.log(plan);
    Go
    err := kl.Environments.Restore(kloudlite.RestoreEnvironment{Environment: "acme/dev", Snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", Discard: []string{"redis"}})if err != nil {    log.Fatal(err)}
    Ruby
    plan = kl.environments.restore(environment: "acme/dev", snapshot: "env-1f2e3d4c5b6a7988-8a7b6c5d", discard: ["redis"])p plan
    Java
    String plan = kl.environments().restore(new Kloudlite.RestoreEnvironment().environment("acme/dev").snapshot("env-1f2e3d4c5b6a7988-8a7b6c5d").discard("redis"));System.out.println(plan);
    CLI
    kode environments restore acme/dev --snapshot env-1f2e3d4c5b6a7988-8a7b6c5d --discard redis

Close-out

  1. Read rolled_back, added, removed, and discarded in the answer.
  2. Wait until the state of the environment is running.

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 an environment named "acme-dev" already exists hereThe 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 QuotaThe 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 foundThe 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/nameYou and a team both have an environment with this name.Use team/name.
409 with the storage pool is full; retry later or ask an operatorThe storage pool of the region cannot hold the copy.Wait, then try again, or ask an operator.
400 with `owner` and `services` 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/restoreYou cloned an environment from a workspace snapshot.Restore a workspace from it.
404 with not found in a clone or a restoreThe 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 lineage.
409 with these services keep data that the snapshot does not have; name them in `discard` to lose itThe 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: redisdiscard 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 insteadThe snapshot froze no service list.Clone the snapshot into a new environment.
409 with api is intercepted by …; release the intercept before restoringA service that the restore removes has an intercept.Remove the intercept, then restore again.
409 with not ready yet: no volume for this workspaceThe environment has no disk yet.Wait until it is ready, then restore again.
state stays creating and placed is NoCapacityThe platform has no capacity right now.Wait, or stop an environment that you do not need.