Skip to content
Docs menu / k8s

@konfig.ts/k8s

@konfig.ts/k8s builds Kubernetes resources in TypeScript, with one twist: wherever a resource points at another resource, it takes a branded reference instead of a raw string. An env var that reads from a Secret, a volume that mounts a ConfigMap, an Ingress TLS block that names a Secret, an imagePullSecrets entry: each of these needs a reference produced by the resource it points at. A workload that references a Secret nobody creates is therefore a compile error, not a failed argocd sync. The package also hosts Environment.bind and Secret.bind, the step that takes an environment contract from @konfig.ts/env and turns it into a Deployment env block plus whatever custom resources the chosen secret backend emits.

Install

Terminal window
bun add @konfig.ts/k8s

Usage

The API module of the full-stack example. It binds the env contract, defines a container whose env mixes the bound vars with a Secret key and a ConfigMap key, and wraps the container in Workload.web, which produces a Deployment and a Service.

infra/modules/api.ts
const apiContainer = Container.define({
  name,
  image: apiImage,
  ports: [Port.make({ name: "http", containerPort: 8080 })],
  env: [
    ...bound.envVars,
    ...EnvVar.secretEnv(bound.members.db.ref, { DATABASE_URL_PRIMARY: "url" }, { podNamespace: namespace }),
    ...EnvVar.configMapEnv(featureFlags.ref, { FEATURE_NEW_UI: "NEW_UI" }),
    EnvVar.value({ name: "API_NAME", value: name })
  ],
  readinessProbe: {
    httpGet: { path: "/healthz", port: Port.ref("http") },
    periodSeconds: 5
  }
})

const workload = Workload.web({
  name,
  namespace,
  deployment: {
    replicas: opts.replicas,
    imagePullSecrets: [{ name: ghcrRef }],
    containers: [apiContainer]
  },
  service: {
    ports: [{ port: 80, targetPort: Port.ref("http") }]
  }
})

Two of the type-level checks are visible here. EnvVar.fromSecretForPod requires podNamespace to match the Secret’s namespace, so a pod cannot read a Secret from another namespace. Port.ref("http") only accepts a name that was declared in ports, so a probe or Service cannot point at a port that does not exist.

Surface

ExportPurpose
Container, Pod, PodSetContainer.define, pod specs, and shared pod-set inputs for workloads
PortPort.make({ name, containerPort }), Port.ref(name) for probes and Services
EnvVarvalue, secretEnv, configMapEnv, fromSecret, fromSecretForPod, fromConfigMap, raw; duplicate names in one container are a compile error at Container.define
Secret, ConfigMap, Namespace, ServiceAccountIdentity constructors exposing a typed .ref; Secret also carries Secret.define and Secret.bind
EnvironmentEnvironment.define re-exported with bind (manifest side) and runtime (process side)
NativeSecretNativeSecret.backend({ type?, immutable?, silenceWarning? }): built-in backend that emits a plain Secret from a source
WorkloadWorkload.web (Deployment + Service + optional Ingress, reloader: "stakater"), Workload.cron (CronJob + ServiceAccount)
Deployment, StatefulSet, Job, CronJob, Service, IngressLower-level resource constructors
Role, RoleBinding, ClusterRole, ClusterRoleBinding, NetworkPolicy, PersistentVolume, PersistentVolumeClaimRBAC, policy, and storage constructors
Volume, SelectorTyped volumes/mounts; label selectors shared between workloads and NetworkPolicy peers
SecretRef, ConfigMapRef, PvcRef, ServiceAccountRefBrand constructors; SecretRefName, ConfigMapRefName, PvcRefName type helpers
hashSecretValuesStamp a pod-spec hash so pods roll on re-render
SecretBackend, BackendEmitInput, BackendTagThe contract secret backend packages implement
K8sRaw Kubernetes types (kubernetes-types@1.30)

Errors

This package exports no tagged error classes of its own. When a bind or render fails, the error comes from one of two places: the secret backend in use (@konfig.ts/sops, @konfig.ts/sealed-secrets), or @konfig.ts/env, which raises SecretSourceError when a plaintext source cannot be resolved and EnvNameCollision when two entries claim the same env var name.

Requirements

  • effect@^4.0.0-rc.111 as a peer dependency (Effect 4, release-candidate line).
  • Depends on @konfig.ts/core, @konfig.ts/env, and kubernetes-types.
  • Runtime: Bun recommended; Node >= 23.6 works; Node 22.6 to 23.5 with --experimental-strip-types; tsx works.

Source and README: packages/k8s.