Kubernetes.Workloads reference
Deployment
Section titled “Deployment”Source:
src/Kubernetes/Deployment.ts
A replicated Kubernetes server on any cluster, running a container image or an HTTP server written in Effect.
Deployment provisions a Kubernetes Deployment + Service (+
ServiceAccount) via server-side apply, with the container image from
exactly one of three sources flat on props: main (bundle an inline
Effect program), context (build your own Dockerfile), or image (a
pre-built registry reference). main and context images are built on
the deploying machine and pushed to the connection’s registry
(Kubernetes.LocalCluster includes one; EKS uses ECR; GKE uses
Artifact Registry). Bindings attach environment variables on any
cluster; cloud credential grants need a platform with workload identity.
On AWS.EKS.Cluster targets it accepts the { env, policyStatements }
contract of AWS.Lambda.Function: IAM policy statements go to a
generated pod-identity role. On GCP.Container.Cluster targets it
accepts the { env, iam } contract of GCP.Run.Service: IAM roles are
granted to the Kubernetes ServiceAccount’s Workload Identity Federation
principal.
Deployment: Creating a Deployment
Section titled “Deployment: Creating a Deployment”Run an image on a local cluster
const cluster = yield* Kubernetes.LocalCluster("Cluster", { name: "alchemy",});
const web = yield* Kubernetes.Deployment("Web", { cluster, image: "ghcr.io/stefanprodan/podinfo:6.15.0", replicas: 2, port: 9898, serviceType: "ClusterIP",});web.serviceName; // K8s-native attrsAny cluster via kubeconfig
const cluster = Kubernetes.KubeConfig({ context: "prod-east" });
const nginx = yield* Kubernetes.Deployment("Nginx", { cluster, image: "nginx:1.27", replicas: 3, port: 80, serviceType: "LoadBalancer",});nginx.url; // the load balancer's addressBuild your own Dockerfile
const legacy = yield* Kubernetes.Deployment("LegacyApp", { cluster, context: "./legacy", replicas: 2, port: 8080,});Deployment: Effect Servers
Section titled “Deployment: Effect Servers”Inline Effect server
const api = yield* Kubernetes.Deployment( "Api", { cluster, main: import.meta.url, port: 3000, replicas: 2 }, Effect.gen(function* () { return { fetch: Effect.succeed(HttpServerResponse.text("ok")), }; }),);Push to your own registry
const cluster = Kubernetes.KubeConfig({ context: "prod-east", registry: { server: "ghcr.io/acme" },});
const api = yield* Kubernetes.Deployment( "Api", { cluster, main: import.meta.url, port: 3000 }, Effect.gen(function* () { return { fetch: Effect.succeed(HttpServerResponse.text("ok")), }; }),);DynamoDB binding on EKS
const cluster = yield* AWS.EKS.Cluster("Cluster", { compute: "auto" });
const api = yield* Kubernetes.Deployment( "Api", { cluster, main: import.meta.url, port: 3000 }, Effect.gen(function* () { const putItem = yield* AWS.DynamoDB.PutItem(table); return { fetch: Effect.gen(function* () { yield* putItem({ Item: { id: { S: "1" } } }); return HttpServerResponse.text("ok"); }), }; }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)),);Tagged Effect server
export class Api extends Kubernetes.Deployment<Api, { health: () => Effect.Effect<string>;}>()("Api") {}
export default Api.make( { cluster, main: import.meta.url, port: 3000 }, Effect.gen(function* () { return { fetch: Effect.gen(function* () { return HttpServerResponse.text("ok"); }), health: () => Effect.succeed("ok"), }; }),);Deployment: Bundling & Tree-shaking
Section titled “Deployment: Bundling & Tree-shaking”main is bundled with rolldown at deploy time. Unused code is
tree-shaken. effect, alchemy, and @distilled.cloud are marked
pure so unused parts prune more aggressively. Your app is not
marked pure.
Mark additional packages as pure
Only list packages with no top-level side effects.
{ main: import.meta.url, build: { pure: { packages: ["my-lib", "@my-scope/*"] }, },}Turn it off
{ main: import.meta.url, build: { pure: false },}Deployment: Kubernetes Escape Hatch
Section titled “Deployment: Kubernetes Escape Hatch”const tuned = yield* Kubernetes.Deployment("Api", { cluster, main: import.meta.url, port: 3000, podTemplate: { spec: { tolerations: [{ key: "gpu", operator: "Exists" }], nodeSelector: { pool: "arm" }, }, },});Source:
src/Kubernetes/Job.ts
Run-to-completion work on any Kubernetes cluster, as a container image or an Effect program.
Job provisions a Kubernetes Job (or CronJob when schedule is
set) via server-side apply and a ServiceAccount, plus — through the
target cluster’s platform adapter — workload identity and a container
image from exactly one of three sources flat on props: main (bundle an
inline Effect program whose impl returns { run }), context (build
your own Dockerfile), or image (a pre-built registry reference).
main and context images are built on the deploying machine and
pushed to the connection’s registry (Kubernetes.LocalCluster includes
one; EKS uses ECR; GKE uses Artifact Registry). On AWS.EKS.Cluster
targets, bindings attach IAM policy statements to a generated
pod-identity role; on GCP.Container.Cluster targets, GCP bindings grant
their IAM roles to the Kubernetes ServiceAccount’s Workload Identity
Federation principal — exactly like Kubernetes.Deployment.
Job: Creating a Job
Section titled “Job: Creating a Job”Remote image (external — no Effect runtime in the container)
const migrate = yield* Kubernetes.Job("DbMigrate", { cluster, image: "ghcr.io/acme/migrator:v3", backoffLimit: 2,});Inline Effect program
const cluster = yield* Kubernetes.LocalCluster("Cluster", { name: "alchemy",});
const hello = yield* Kubernetes.Job( "Hello", { cluster, main: import.meta.url, backoffLimit: 2 }, Effect.gen(function* () { return { run: Effect.log("hello from a Job"), }; }),);Inline Effect program with a DynamoDB binding (EKS)
const seed = yield* Kubernetes.Job( "SeedData", { cluster, main: import.meta.url }, Effect.gen(function* () { const putItem = yield* AWS.DynamoDB.PutItem(table); return { run: Effect.gen(function* () { yield* putItem({ Item: { id: { S: "seed" } } }); }), }; }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)),);Tagged Effect program
export class Backfill extends Kubernetes.Job<Backfill, { progress: () => Effect.Effect<number>;}>()("Backfill") {}
export default Backfill.make( { cluster, main: import.meta.url, backoffLimit: 1 }, Effect.gen(function* () { return { run: Effect.gen(function* () { }), progress: () => Effect.succeed(0), }; }),);Job: Bundling & Tree-shaking
Section titled “Job: Bundling & Tree-shaking”main is bundled with rolldown at deploy time. Unused code is
tree-shaken. effect, alchemy, and @distilled.cloud are marked
pure so unused parts prune more aggressively. Your app is not
marked pure.
Mark additional packages as pure
Only list packages with no top-level side effects.
{ main: import.meta.url, build: { pure: { packages: ["my-lib", "@my-scope/*"] }, },}Turn it off
{ main: import.meta.url, build: { pure: false },}Job: Scheduling
Section titled “Job: Scheduling”const nightly = yield* Kubernetes.Job("NightlyBackfill", { cluster, main: import.meta.url, schedule: "0 3 * * *",});