Services
Add, change, and remove the services of an environment.
Last updated
Overview
A service is one program of an environment, run from a container image. The environment always takes the whole list of services: the platform starts new services, restarts changed ones, and stops the ones left out.
| Field | Required | Description |
|---|---|---|
name | Yes | The service name, which is also its host name. |
image | Yes | The image, for example postgres:16. It is pinned to its digest when stored. A private image of a connected container registry of the team is pulled with its login. |
ports | No | The ports the service listens on. Other services and connected workspaces reach it at name:port. |
env | No | Variables. A value of ${{ secrets.NAME }} refers to a secret of the owner. |
mounts | No | A folder on the environment disk and the path where the service sees it. Data outside a mount is lost on restart. |
command | No | The command to run. Empty keeps the image's command. |
resources | No | vCPU and memory. The default size applies without it. |
A removed service keeps its folders on the disk, so adding it again with the same mounts finds its data.
| Item | Limit |
|---|---|
| Name | 1 to 40 lowercase letters, digits, or -, unique in the environment. |
| Port | 1 to 65535. |
| Variable name | Letters, digits, and _, not starting with a digit. |
| Mount folder | One name of letters, digits, ., _, and -. |
| Mount path | An absolute path without :. |
To do these tasks with an AI agent, see Agentic workflows.
Tasks
Add a service
Reason for the task
Run another program in an environment.
Job set-up
- Access: you. A team environment needs team membership.
- References:
PATCH /v1/environments/{id}.
Procedure
Add the service. With the SDK, read the current list and send it back with the new service.
from kloudlite import Kodekl = Kloudlite()env = kl.environments.get(environment="acme-dev")services = env["services"] + [{"name": "cache", "image": "redis:7", "ports": [6379]}]kl.environments.set_services(environment="acme-dev", services=services)import { Kode, type Service } from "./index.ts";const kl = new Kloudlite();const env = (await kl.environments.get({ environment: "acme-dev" })) as { services: Service[] };const services = [...env.services, { name: "cache", image: "redis:7", ports: [6379] }];await kl.environments.setServices({ environment: "acme-dev", services });kl := kloudlite.New(kloudlite.Options{})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)}services = append(services, kloudlite.Service{Name: "cache", Image: "redis:7", Ports: []int{6379}})if err := kl.Environments.SetServices(kloudlite.SetServices{Environment: "acme-dev", Services: services}); err != nil { log.Fatal(err)}require_relative "kloudlite"kl = Kloudlite::Client.newenv = kl.environments.get(environment: "acme-dev")services = env["services"] + [{ name: "cache", image: "redis:7", ports: [6379] }]kl.environments.set_services(environment: "acme-dev", services: services)var kl = new Kloudlite();// Replaces the list: describe every service that stays, and the new one.var db = new Kloudlite.Service().name("db").image("postgres:16").port(5432) .env("POSTGRES_PASSWORD", "dev").mount("pgdata", "/var/lib/postgresql/data");var cache = new Kloudlite.Service().name("cache").image("redis:7").port(6379);kl.environments().setServices(new Kloudlite.SetServices().environment("acme-dev").services(db, cache));# Replaces the list: send every service, including the new one.kode environments set-services acme-dev --services '[ {"name": "db", "image": "postgres:16", "ports": [5432], "env": {"POSTGRES_PASSWORD": "dev"}, "mounts": [{"folder": "pgdata", "path": "/var/lib/postgresql/data"}]}, {"name": "cache", "image": "redis:7", "ports": [6379]}]'
Close-out
- Make sure that
service_statusshowscacheas ready.
Change a service
Reason for the task
Change the image, variables, ports, or mounts of a service.
Job set-up
- Access: you. A team environment needs team membership.
- References:
PATCH /v1/environments/{id}.
The service restarts and its connections end. Data in its mounted folders is kept.
Procedure
Send the changed service, here
dbonpostgres:17.env = kl.environments.get(environment="acme-dev")services = [{**sv, "image": "postgres:17"} if sv["name"] == "db" else sv for sv in env["services"]]kl.environments.set_services(environment="acme-dev", services=services)const env = (await kl.environments.get({ environment: "acme-dev" })) as { services: Service[] };const services = env.services.map((sv) => (sv.name === "db" ? { ...sv, image: "postgres:17" } : sv));await kl.environments.setServices({ environment: "acme-dev", services });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 == "db" { services[i].Image = "postgres:17" }}if err := kl.Environments.SetServices(kloudlite.SetServices{Environment: "acme-dev", Services: services}); err != nil { log.Fatal(err)}env = kl.environments.get(environment: "acme-dev")services = env["services"].map { |sv| sv["name"] == "db" ? sv.merge("image" => "postgres:17") : sv }kl.environments.set_services(environment: "acme-dev", services: services)// Replaces the list: describe every service, with the new image for db.var db = new Kloudlite.Service().name("db").image("postgres:17").port(5432) .env("POSTGRES_PASSWORD", "dev").mount("pgdata", "/var/lib/postgresql/data");var cache = new Kloudlite.Service().name("cache").image("redis:7").port(6379);kl.environments().setServices(new Kloudlite.SetServices().environment("acme-dev").services(db, cache));kode environments set-services acme-dev --services '[ {"name": "db", "image": "postgres:17", "ports": [5432], "env": {"POSTGRES_PASSWORD": "dev"}, "mounts": [{"folder": "pgdata", "path": "/var/lib/postgresql/data"}]}, {"name": "cache", "image": "redis:7", "ports": [6379]}]'
Close-out
- Make sure that the service shows the new value and is ready.
Remove a service
Reason for the task
Stop a program and take it out of an environment.
Job set-up
- Access: you. A team environment needs team membership.
- References:
PATCH /v1/environments/{id}.
Procedure
Send the list without the service. Its folders stay on the disk.
env = kl.environments.get(environment="acme-dev")services = [sv for sv in env["services"] if sv["name"] != "cache"]kl.environments.set_services(environment="acme-dev", services=services)const env = (await kl.environments.get({ environment: "acme-dev" })) as { services: Service[] };await kl.environments.setServices({ environment: "acme-dev", services: env.services.filter((sv) => sv.name !== "cache") });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)}kept := services[:0]for _, sv := range services { if sv.Name != "cache" { kept = append(kept, sv) }}if err := kl.Environments.SetServices(kloudlite.SetServices{Environment: "acme-dev", Services: kept}); err != nil { log.Fatal(err)}env = kl.environments.get(environment: "acme-dev")kl.environments.set_services(environment: "acme-dev", services: env["services"].reject { |sv| sv["name"] == "cache" })// Replaces the list: describe every service that stays.var db = new Kloudlite.Service().name("db").image("postgres:16").port(5432) .env("POSTGRES_PASSWORD", "dev").mount("pgdata", "/var/lib/postgresql/data");kl.environments().setServices(new Kloudlite.SetServices().environment("acme-dev").services(db));kode environments set-services acme-dev --services '[ {"name": "db", "image": "postgres:16", "ports": [5432], "env": {"POSTGRES_PASSWORD": "dev"}, "mounts": [{"folder": "pgdata", "path": "/var/lib/postgresql/data"}]}]'
Close-out
- Make sure that the service is no longer in
services.
Troubleshooting
| Symptom | Possible cause | Corrective action |
|---|---|---|
400 naming a field of a service | A name, port, variable, or mount breaks the rules in Overview. | Correct the field. |
422 with `DB_PASSWORD` is not a secret of acme | The secret does not exist for the environment's owner. | Set the secret. |
422 with image not found in its registry | The registry does not hold the image or tag. | Correct the image name or tag. |
409 with cpu: 8 of 8 in use; request more under Quota | The service does not fit in the quota. | Remove an unused service, or request more quota. |
409 with cache is intercepted by ws-…; release the intercept before removing the service | A workspace intercepts the service. | Release the intercept, then remove the service. |