Skip to content
DocsAll docs
Console

Env Vars

Give a value to the processes of a workspace, a sandbox, a service or a pipeline job, as a plain variable or from a secret.

Last updated

On this page

Description and operation

General

A process reads its settings from environment variables. You give the variables of a workspace, a sandbox, a service or a pipeline job in its env field. A variable holds its value in plain text, or refers to a secret.

A secret is a named value that the platform keeps for an owner, such as a password, a token or a connection string. You set it once and refer to it by name. The platform puts the value in the variable when the process starts. The value never shows in a definition, a document or a log.

AspectVariableSecret
What it isA NAME=value pair in the env of one resource. See Variables.A named value of an owner, used by many resources. See Secrets.
Where the value is keptIn the definition of the resource, in plain text.In the secret store of the owner. The resource keeps only the reference.
Who can read the valueEverybody who can read the resource, and its processes.Only the processes of the resources that refer to it.
Shown by the APIFor a service, in the environment document.Never. A list shows only the names, who changed each one and when.
Set withThe env field of the resource.kloudlite secrets set, the SDK or PUT /v1/secrets/{owner}/{name}.

Personal secrets in a team

A personal secret in a team is a secret that you keep for yourself inside one team, for example your own access token. Only you can set it, show its name or delete it. See Set a personal secret in a team.

Only your own resources in that team read it: your team workspaces, benches and sandboxes. Resources that the team shares never read it, so your value never goes to another member.

ResourceReads your personal secrets in the team
Your workspace, bench or sandbox in the teamYes
Workspace, bench or sandbox of another memberNo
Service of an environmentNo. It reads only the secrets of the team.
Pipeline jobNo. It reads only the secrets of the owner of the pipeline.
Your personal workspace or sandboxNo. It reads only your own secrets.

A reference in your resource of the team finds the value in this order.

OrderStoreResult
1Your secrets in the teamYour value is used, also when the team has the same name.
2The secrets of the teamThe value of the team is used.
3Neither store has the nameThe resource does not start. It stays in the state creating.

Use a variable for a setting such as LOG_LEVEL. Use a secret for a password, a key or a token.

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

Reference syntax

A reference is a variable value that is exactly this text, where NAME is the name of a secret.

text
${{ secrets.NAME }}
RuleDescription
Whole valueThe reference must be the whole value. With text around it, for example postgres://${{ secrets.DB_PASS }}@db, it is plain text. A pipeline refuses it.
BlanksThe blank characters inside the braces are optional. ${{secrets.DB_URL}} is also a reference.
Secret nameA to Z, 0 to 9 and _. It does not start with a digit.
Variable nameThe variable can have a different name from the secret, for example DATABASE_URL: ${{ secrets.DB_URL }}.
CheckThe platform checks each reference when you save the resource. If no store that the resource reads has the name, the call fails with 422 and saves nothing.

Owner and scope

A resource reads the secrets of its own owner. A personal resource reads only your secrets. A team resource reads the secrets of the team. Your own team workspace, bench or sandbox reads your personal secrets in the team first. A name is unique in one store, so you, your team and your personal secrets in the team can each have a secret DB_URL.

ResourceStore of the secrets that it reads
Personal workspace or sandboxYour secrets
Your team workspace, bench or sandboxYour personal secrets in the team, then the secrets of the team
Service of an environmentThe owner of the environment
Pipeline jobThe owner of the pipeline

Delivery

A reference is valid in these fields. The value reaches the process as an environment variable.

ResourceFieldTask
Workspaceenv of the workspace, at creation or laterUse a secret in a workspace
Service of an environmentenv of the serviceUse a secret in a service
Pipeline jobenv of the job or of a step, and the secrets list of the jobUse a secret in a pipeline
Sandboxenv, set at creationUse a secret in a sandbox

The SSH sessions of a workspace start from the tool server, so they have the same variables as its processes.

When a new value takes effect

A resource reads a secret when its processes start. A replaced value does not change a process that runs, and the platform does not restart it. To apply the value, restart the resource, or set restartOnEnvChange to true to restart it each time.

ResourceWhen a new value arrives
Service of an environmentAt the next start. A running service shows restart_required until then.
WorkspaceAt the next start. A running workspace shows restart_required until then. A change of env follows the same rule.
SandboxNever. A sandbox keeps the value that it started with.
Pipeline jobWhen the job starts.

Pipelines

RuleDescription
MaskingThe log shows *** for each secret value that the job holds. A value shorter than 4 bytes, or an encoded form of a value, is not masked.
SaveA save fails with 422 when a secret that the definition names does not exist for the owner.
DeleteA delete of a secret fails with 409 while the newest version of a pipeline, or a queued or running run, refers to it.
WhereA reference is valid only in the env of a job or a step, and in the secrets list of a job. In a run line it is an error.

Quota and limits

Secrets do not count against the quota of the owner. See also Limits.

ItemLimit
Secrets100 for each store: you, a team, and you in a team.
Secret value64 KiB.
Variables of a workspace or a sandbox64, each value up to 32 KiB.

Tasks

Use a secret in a workspace

Reason for the task: Give a workspace the value of a secret as a variable.

Job set-up

CAUTION

The call replaces all variables of the workspace. A variable that is not in the new list is removed.

Procedure

  1. Send the complete list of variables, with the reference.

    Python
    from kloudlite import Kloudlitekl = Kloudlite()kl.workspaces.set_env(workspace="acme/api", env={"DB_URL": "${{ secrets.DB_URL }}"})
    TypeScript
    import { Kloudlite } from "./index.ts";const kl = new Kloudlite();await kl.workspaces.setEnv({ workspace: "acme/api", env: { DB_URL: "${{ secrets.DB_URL }}" } });
    Go
    kl := kloudlite.New(kloudlite.Options{})if _, err := kl.Workspaces.SetEnv("acme/api", map[string]string{"DB_URL": "${{ secrets.DB_URL }}"}); err != nil {    log.Fatal(err)}
    Ruby
    require_relative "kloudlite"kl = Kloudlite::Client.newkl.workspaces.set_env(workspace: "acme/api", env: { "DB_URL" => "${{ secrets.DB_URL }}" })
    Java
    var kl = new Kloudlite();kl.workspaces().setEnv("acme/api", Map.of("DB_URL", "${{ secrets.DB_URL }}"));
    CLI
    kloudlite workspaces set-env acme/api --env 'DB_URL=${{ secrets.DB_URL }}'
  2. Stop the workspace, then start it. See Stop a workspace and Start a workspace.

Close-out

  1. Run test -n "$DB_URL" && echo set in the workspace. Make sure that the output is set.

Use a secret in a service

Reason for the task: Give a service of an environment the value of a secret as a variable.

Job set-up

NOTE

A running service keeps its old variables until you restart it.

Procedure

  1. Send the service list with the reference in the env of the service, here api.

    Python
    env = kl.environments.get(environment="acme-dev")services = [    {**sv, "env": {**sv["env"], "DB_URL": "${{ secrets.DB_URL }}"}} if sv["name"] == "api" else sv    for sv in env["services"]]kl.environments.set_services(environment="acme-dev", services=services)
    TypeScript
    import { type Service } from "./index.ts";const env = (await kl.environments.get({ environment: "acme-dev" })) as { services: Service[] };const services = env.services.map((sv) =>  sv.name === "api" ? { ...sv, env: { ...sv.env, DB_URL: "${{ secrets.DB_URL }}" } } : sv,);await kl.environments.setServices({ environment: "acme-dev", services });
    Go
    env, err := kl.Environments.Get("acme-dev")if err != nil {    log.Fatal(err)}var services []kloudlite.Serviceraw, _ := json.Marshal(env["services"])if err := json.Unmarshal(raw, &services); err != nil {    log.Fatal(err)}for i := range services {    if services[i].Name == "api" {        if services[i].Env == nil {            services[i].Env = map[string]string{}        }        services[i].Env["DB_URL"] = "${{ secrets.DB_URL }}"    }}if err := kl.Environments.SetServices(kloudlite.SetServices{Environment: "acme-dev", Services: services}); err != nil {    log.Fatal(err)}
    Ruby
    env = kl.environments.get(environment: "acme-dev")services = env["services"].map do |sv|  sv["name"] == "api" ? sv.merge("env" => sv["env"].merge("DB_URL" => "${{ secrets.DB_URL }}")) : svendkl.environments.set_services(environment: "acme-dev", services: services)
    Java
    // Replaces the list: describe every service, with the reference in api.var api = new Kloudlite.Service().name("api").image("ghcr.io/acme/api:1.0").port(8080)    .env("DB_URL", "${{ secrets.DB_URL }}");kl.environments().setServices(new Kloudlite.SetServices().environment("acme-dev").services(api));
    CLI
    # Replaces the list: send every service, with the reference in api.kloudlite environments set-services acme-dev --services '[  {"name": "api", "image": "ghcr.io/acme/api:1.0", "ports": [8080], "env": {"DB_URL": "${{ secrets.DB_URL }}"}}]'

Close-out

  1. Get the environment again. Make sure that service_status shows api as ready.

Use a secret in a pipeline

Reason for the task: Give the steps of a pipeline job the value of a secret as a variable.

Job set-up

Procedure

  1. Add the name of the secret to the secrets list of the job. The variable has the same name.

    json
    { "jobs": { "deploy": { "secrets": ["KUBECONFIG_PROD"] } } }
  2. Give a secret to one step under another variable name. Put the reference alone in the value.

    json
    { "steps": [{ "run": "./deploy.sh", "env": { "TOKEN": "${{ secrets.DEPLOY_TOKEN }}" } }] }
  3. Send the definition as the next version of the pipeline.

Close-out

  1. Start a run and read the log. Make sure that it shows *** for each secret value.

Use a secret in a sandbox

Reason for the task: Give the main process of a sandbox the value of a secret as a variable.

Job set-up

NOTE

This task creates a standalone sandbox. A sandbox in a session takes the same env. See Run a sandbox in a session.

Procedure

  1. Create the sandbox with the reference in env. Add team for a team sandbox.

    Python
    sb = kl.sandboxes.create(cmd=["sh", "-c", 'test -n "$DB_URL" && echo set'], env={"DB_URL": "${{ secrets.DB_URL }}"})
    TypeScript
    const sb = await kl.sandboxes.create({ cmd: ["sh", "-c", 'test -n "$DB_URL" && echo set'], env: { DB_URL: "${{ secrets.DB_URL }}" } });
    Go
    sb, err := kl.Sandboxes.Create(kloudlite.CreateSandbox{Cmd: []string{"sh", "-c", `test -n "$DB_URL" && echo set`}, Env: map[string]string{"DB_URL": "${{ secrets.DB_URL }}"}})if err != nil {    log.Fatal(err)}
    Ruby
    sb = kl.sandboxes.create(cmd: ["sh", "-c", 'test -n "$DB_URL" && echo set'], env: { "DB_URL" => "${{ secrets.DB_URL }}" })
    Java
    var sb = kl.sandboxes().create(new Kloudlite.CreateSandbox().cmd(List.of("sh", "-c", "test -n \"$DB_URL\" && echo set")).env(Map.of("DB_URL", "${{ secrets.DB_URL }}")));
    CLI
    kloudlite sandboxes create --env 'DB_URL=${{ secrets.DB_URL }}' -- sh -c 'test -n "$DB_URL" && echo set'

Close-out

  1. Read the output of the sandbox as in Read the output and the exit code. Make sure that the output is set.

Troubleshooting

SymptomPossible causeCorrective action
422 with `DB_URL` is not a secret of acmeThe owner of the resource has no secret of that name.Set the secret for the owner that the message names.
422 with `DB_URL` is not a secret of acme or yoursNeither you in the team nor the team has a secret of that name.Set your secret in the team, or set the secret of the team.
A service does not get your personal secret in the teamA service reads only the secrets of the team.Set the secret of the team with --scope team.
A workspace stays in the state creating after a startA secret that its env refers to was deleted after the save. For your team workspace, the name is in neither your store in the team nor the store of the team.Show the secrets and set the missing one.
A running workspace shows the old valueA workspace reads a secret only at start.Stop the workspace and start it again.
The variable holds the text ${{ secrets.NAME }}The value has text around the reference, so it is plain text.Put the reference alone in the value.
A pipeline shows unknown identifier `secrets.NAME` The reference is in a run line, or has text around it.Put the reference alone in the value of an env entry.
422 with secret NAME does not exist when you save a pipelineThe owner of the pipeline has no secret of that name.Set the secret for that owner, then save again.