Reference
Every callable in the library, generated from the same catalog the
Assembler
reads — so it never drifts from the code. The catalog
itself is published at /catalog.json
for programmatic use.
Workloads
Deployable starting points. Import a stage by its canonical path, compose
+ features, and render with kurly.list.
cnpg-cluster
A highly-available PostgreSQL cluster as a CloudNativePG Cluster custom resource (three instances, a bootstrapped database, a PodMonitor). Requires the CloudNativePG operator.
cluster cnpg
The PostgreSQL Cluster CR. Adapt it with the parameters and render with kurly.list — composed by parameter, not by + feature (it is a custom resource, not a base kind).
Import: import 'github.com/metio/kurly/workloads/cnpg-cluster/cluster.libsonnet'
| Parameter | Type | Required | Default |
|---|---|---|---|
name | string | — | "postgres" |
instances | int | — | 3 |
storageSize | quantity | — | "10Gi" |
database | string | — | "app" |
tik
A lightweight ticket board and release supervisor. One process serves a read-only board and runs the store's writers over a shared append-only event store.
backend http
The tik backend supervisor: a single-writer http app over a ReadWriteOnce store (one replica, recreated to avoid deadlocking on the volume). Compose an exposure recipe to serve the board.
Import: import 'github.com/metio/kurly/workloads/tik/backend.libsonnet'
| Parameter | Type | Required | Default |
|---|---|---|---|
image | string | — | "ghcr.io/metio/tik:2026.7.14194001" |
valkey
A persistent Valkey server (the BSD Redis fork) on the official upstream image, as a kurly.stateful workload with a per-pod PVC and a headless Service. Single-instance stage; a Redis-compatible alternative runs by overriding the image.
cache worker
An in-memory Valkey cache that upgrades its version with zero downtime and no data loss, on the stock image and no orchestrator — the replication hand-off lives entirely in the pod manifests (headless Service, maxSurge, an initContainer that replicates the running peer, and a preStop failover).
Import: import 'github.com/metio/kurly/workloads/valkey/cache.libsonnet'
| Parameter | Type | Required | Default |
|---|---|---|---|
image | string | — | "docker.io/valkey/valkey:8" |
maxMemory | string | — | "256mb" |
instance stateful
The single-instance Valkey server: a StatefulSet with append-only persistence into a volumeClaimTemplate. Compose + features as usual (it is a composable kurly.stateful app).
Import: import 'github.com/metio/kurly/workloads/valkey/instance.libsonnet'
| Parameter | Type | Required | Default |
|---|---|---|---|
image | string | — | "docker.io/valkey/valkey:8" |
storageSize | quantity | — | "1Gi" |
Kinds
The base workload each recipe starts from.
kurly.cron(name, image, schedule)
A scheduled job: a CronJob that runs to completion on a cron schedule.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
name | string | yes | — | "nightly-report" |
image | string | yes | — | "ghcr.io/acme/report:1.2.3" |
schedule | string | yes | — | "0 2 * * *" |
kurly.daemon(name, image)
A per-node agent: a DaemonSet running one pod on every node.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
name | string | yes | — | "node-exporter" |
image | string | yes | — | "ghcr.io/acme/node-agent:1.2.3" |
kurly.http(name, image)
An HTTP workload: a Deployment (2 replicas) and a ClusterIP Service. Compose an exposure feature to accept traffic from outside the cluster.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
name | string | yes | — | "storefront" |
image | string | yes | — | "ghcr.io/acme/storefront:1.2.3" |
kurly.job(name, image)
A one-off task that runs to completion: a Job with restartPolicy OnFailure. No Service, no replicas.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
name | string | yes | — | "db-migrate" |
image | string | yes | — | "ghcr.io/acme/migrate:1.2.3" |
kurly.stateful(name, image)
A workload with stable identity and per-pod storage: a StatefulSet plus the headless Service that names it. The store feature renders as a per-pod volumeClaimTemplate.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
name | string | yes | — | "postgres" |
image | string | yes | — | "ghcr.io/acme/postgres:16" |
kurly.worker(name, image)
A background worker: a Deployment with no Service. Reaches out (queues, schedules) rather than serving traffic.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
name | string | yes | — | "indexer" |
image | string | yes | — | "ghcr.io/acme/indexer:1.2.3" |
Features
Composable + capabilities. Each contributes only to the merged
config, so features late-bind regardless of compose order.
container
kurly.annotations(annotations)
Extra annotations on metadata and the pod template.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
annotations | object | yes | — | {"prometheus.io/scrape":"true"} |
kurly.args(args)
Appends arguments to the image's own entrypoint — typically a subcommand selecting the workload.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
args | array | yes | — | ["backend","--config=/etc/tik/pipelines.edn"] |
kurly.command(command)
Overrides the image entrypoint entirely.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
command | array | yes | — | ["/bin/app"] |
kurly.env(env)
Environment variables as a name→value map, appended to the container.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
env | object | yes | — | {"LOG_LEVEL":"info"} |
kurly.image(image)
Overrides the container image.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
image | string | yes | — | "ghcr.io/acme/app:1.2.3" |
kurly.imagePullSecrets(names)
Names of existing Secrets the kubelet uses to pull the image.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
names | array | yes | — | ["regcred"] |
kurly.initContainer(container)
An init container run to completion before the main one — the full container spec, passed through. Composes more than once.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
container | object | yes | — | {"command":["sh","-c","echo ready"],"image":"busybox:1","name":"setup"} |
kurly.labels(labels)
Extra labels on metadata and the pod template (never on immutable selectors).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
labels | object | yes | — | {"team":"payments"} |
kurly.lifecycle(preStop, postStart)
Container lifecycle handlers (postStart / preStop), passed through verbatim.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
preStop | object | — | — | {"exec":{"command":["sh","-c","valkey-cli failover"]}} |
postStart | object | — | — | — |
kurly.livenessProbe(probe)
An explicit liveness probe spec (exec/tcpSocket/httpGet), overriding the default http probe.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
probe | object | yes | — | {"tcpSocket":{"port":6379}} |
kurly.podAnnotations(podAnnotations)
Annotations on the pod template ONLY — for sidecar injection and scrape hints that are meaningless on the controller object.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
podAnnotations | object | yes | — | {"linkerd.io/inject":"enabled"} |
kurly.podLabels(podLabels)
Labels on the pod template ONLY (never the workload metadata or the immutable selector) — for network-policy selectors and log collection.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
podLabels | object | yes | — | {"tier":"database"} |
kurly.port(port)
The container port the workload listens on (also the Service target).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
port | int | yes | — | 8080 |
kurly.priorityClassName(priorityClassName)
The pod's scheduling priority class.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
priorityClassName | string | yes | — | "high-priority" |
kurly.probes(path)
HTTP readiness and liveness probes on the named http port.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
path | path | — | "/healthz" | "/tickets.edn" |
kurly.readinessProbe(probe)
An explicit readiness probe spec (exec/tcpSocket/httpGet), overriding the default http probe.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
probe | object | yes | — | {"exec":{"command":["sh","-c","valkey-cli ping"]}} |
kurly.recreate()
The single-writer strategy: tears the old pod down before starting the new one, so a ReadWriteOnce store never deadlocks a rollout.
kurly.replicas(replicas)
The desired number of pod replicas.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
replicas | int | yes | — | 3 |
kurly.resourcePreset(preset)
A named resource size (nano/micro/small/medium/large) — a memory request equal to its limit and a CPU request with no limit. Replaces resources wholesale.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
preset | string | yes | — | "small" |
kurly.resources(requests, limits)
Container resource requests and/or limits.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
requests | object | — | — | {"cpu":"100m","memory":"128Mi"} |
limits | object | — | — | {"memory":"256Mi"} |
kurly.rollingUpdate(maxSurge, maxUnavailable)
RollingUpdate tuning so a new pod surges alongside the old during an update — the overlap a replication hand-off needs.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
maxSurge | any | — | — | 1 |
maxUnavailable | any | — | — | — |
kurly.serviceAccount(serviceAccountName)
Runs the pod under a named ServiceAccount (also gates token automount).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
serviceAccountName | string | yes | — | "storefront" |
kurly.strategy(strategy)
The Deployment update strategy.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
strategy | string | yes | — | "RollingUpdate" |
kurly.terminationGracePeriod(seconds)
How long the pod gets to shut down gracefully (a preStop hook's window).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
seconds | int | yes | — | 120 |
kurly.version(version)
The workload version, stamped as app.kubernetes.io/version on every object.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
version | string | yes | — | "1.2.3" |
networking
kurly.headlessService(port, publishNotReady)
A headless Service (clusterIP: None) selecting the pods, for DNS peer discovery — the discovery a replication hand-off needs.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
port | int | — | — | 6379 |
publishNotReady | bool | — | false | — |
kurly.networkPolicy(ingress, egress, policyTypes)
A NetworkPolicy firewalling the pods (ingress/egress rules and policyTypes passed through verbatim).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
ingress | array | — | — | — |
egress | array | — | — | — |
policyTypes | array | — | — | — |
observability
kurly.serviceMonitor(port, path, interval)
A Prometheus-Operator ServiceMonitor scraping the workload Service.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
port | string | — | "http" | — |
path | path | — | "/metrics" | — |
interval | string | — | — | — |
placement
kurly.affinity(affinity)
A pod/node affinity object, merged onto the pod template.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
affinity | object | yes | — | {"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"disktype","operator":"In","values":["ssd"]}]}]}}} |
kurly.nodeSelector(nodeSelector)
Restricts the pod to nodes carrying these labels.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
nodeSelector | object | yes | — | {"disktype":"ssd"} |
kurly.tolerations(tolerations)
Tolerations letting the pod schedule onto tainted nodes.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
tolerations | array | yes | — | [{"effect":"NoSchedule","key":"gpu","operator":"Exists"}] |
kurly.topologySpread(constraints)
Topology-spread constraints spreading the pods across a topology domain (keep version-bound labels in the selector so a rollout spreads the new set independently).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
constraints | array | yes | — | [{"labelSelector":{"matchLabels":{"app.kubernetes.io/name":"web"}},"maxSkew":1,"topologyKey":"kubernetes.io/hostname","whenUnsatisfiable":"DoNotSchedule"}] |
reliability
kurly.hpa(minReplicas, maxReplicas, targetCPU, targetMemory)
A HorizontalPodAutoscaler scaling the Deployment on CPU and/or memory utilization.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
minReplicas | int | yes | — | 2 |
maxReplicas | int | yes | — | 10 |
targetCPU | int | — | — | 80 |
targetMemory | int | — | — | — |
kurly.pdb(minAvailable, maxUnavailable)
A PodDisruptionBudget capping voluntary disruption. Set one of minAvailable / maxUnavailable.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
minAvailable | any | — | — | 1 |
maxUnavailable | any | — | — | — |
scheduling
kurly.concurrencyPolicy(concurrencyPolicy)
How to treat a job that is still running when the next is due (Allow/Forbid/Replace).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
concurrencyPolicy | string | yes | — | "Forbid" |
kurly.schedule(schedule)
The cron schedule expression.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
schedule | string | yes | — | "0 2 * * *" |
security
kurly.hostUsers()
Shares the host user namespace (relaxes hostUsers=false).
kurly.rbac(rules)
Mints a ServiceAccount, a namespaced Role with the given rules, and the RoleBinding, and runs the pod under that ServiceAccount.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
rules | array | yes | — | [{"apiGroups":[""],"resources":["configmaps"],"verbs":["get","list","watch"]}] |
kurly.rootUser()
Allows the container to run as root (relaxes runAsNonRoot).
kurly.runAs(uid, gid, fsGroup)
Pins the run-as user/group (and matching fsGroup) for images that do not declare a non-root USER.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
uid | int | yes | — | 12345 |
gid | int | — | — | — |
fsGroup | int | — | — | — |
kurly.writableRootFilesystem()
Makes the root filesystem writable (relaxes readOnlyRootFilesystem).
storage
kurly.config(files, mountPath)
Renders a ConfigMap from a filename→content map and mounts it read-only.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
files | object | yes | — | {"app.conf":"key = value"} |
mountPath | path | — | "/etc/config" | — |
kurly.scratch(mountPath, sizeLimit)
A writable emptyDir — the escape valve a read-only root filesystem needs for /tmp and the like.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
mountPath | path | yes | — | "/tmp" |
sizeLimit | quantity | — | — | — |
kurly.secretMount(secretName, mountPath, readOnly, optional, defaultMode)
Mounts an EXISTING Secret (kurly never mints key material).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
secretName | string | yes | — | "tik-tls" |
mountPath | path | yes | — | "/etc/tls" |
readOnly | bool | — | true | — |
optional | bool | — | false | — |
defaultMode | int | — | — | — |
kurly.store(mountPath, size, accessModes, storageClass, selector, annotations)
The workload's own PersistentVolumeClaim, mounted at a path.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
mountPath | path | yes | — | "/var/lib/tik" |
size | quantity | yes | — | "1Gi" |
accessModes | array | — | ["ReadWriteOnce"] | — |
storageClass | string | — | — | — |
selector | object | — | — | — |
annotations | object | — | — | — |
Exposure
Routing recipes composed onto an http workload. All join the
exposure exclusion group — one exposure per workload.
kurly.expose.gateway(host, gateway, gatewayNamespace, sectionName)
Attaches an HTTPRoute to an existing shared Gateway (the usual platform-team setup).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
host | hostname | yes | — | "storefront.example.com" |
gateway | string | yes | — | "shared" |
gatewayNamespace | string | — | — | — |
sectionName | string | — | — | — |
kurly.expose.ingress(host, ingressClass)
Routes the host to the workload through the Ingress API.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
host | hostname | yes | — | "storefront.example.com" |
ingressClass | string | — | — | — |
kurly.expose.listenerSet(host, listenerSet, listenerSetNamespace, sectionName)
Attaches an HTTPRoute to an existing ListenerSet (per-tenant listener ownership).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
host | hostname | yes | — | "storefront.example.com" |
listenerSet | string | yes | — | "tenant-a" |
listenerSetNamespace | string | — | — | — |
sectionName | string | — | — | — |
kurly.expose.ownGateway(host, gatewayClass)
Generates a dedicated Gateway plus the HTTPRoute — for clusters with no shared Gateway to attach to.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
host | hostname | yes | — | "storefront.example.com" |
gatewayClass | string | yes | — | "istio" |
kurly.expose.ownListenerSet(host, gateway, gatewayNamespace)
Generates a ListenerSet that adds the workload's own listener to a shared Gateway, plus the HTTPRoute. The Gateway must opt in via spec.allowedListeners.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
host | hostname | yes | — | "storefront.example.com" |
gateway | string | yes | — | "shared" |
gatewayNamespace | string | — | — | — |
Security
Pod Security Standards profiles, composed as mixins. The last profile composed wins; single-knob hatches (in Features) fine-tune after it.
kurly.security.baseline
Relaxes what only restricted requires (root allowed, default capabilities kept, privilege escalation and unpinned seccomp permitted); keeps the read-only root filesystem and user namespaces.
kurly.security.privileged
Emits no security fields at all — the manifest constrains nothing.
kurly.security.restricted
The default posture, written out. Compose after another profile to re-tighten.
Helpers
Rendering terminals — turn a composed app or an explicit set of parts into the output a consumer applies or publishes.
kurly.join(parts)
Builds one flat array from parts that may be null (dropped) or nested arrays (flattened one level), for assembling any value with conditionals and optional groups. A Jsonnet `if` with no else is null when false, so an unmet condition drops out.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
parts | array | yes | — | — |
kurly.list(app)
Renders one composed app as a kind: List, including its hidden owned manifests (the store PVC, the config ConfigMap).
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
app | object | yes | — | — |
kurly.listOf(parts)
Renders an explicit set of parts as a kind: List. Joins the parts first, so entries can be null (dropped) or nested arrays (flattened) — build the set with conditionals and optional groups.
| Parameter | Type | Required | Default | Example |
|---|---|---|---|---|
parts | array | yes | — | — |