| 1 | --- |
| 2 | title: Configuration & bindings |
| 3 | description: Pass configuration into Kubernetes workloads with env and Outputs, understand the variables Alchemy sets, keep secrets out of plain text, and use bindings. |
| 4 | --- |
| 5 | |
| 6 | A [Deployment](/kubernetes/workloads/deployments) or |
| 7 | [Job](/kubernetes/workloads/jobs) receives configuration in three |
| 8 | ways. There is the `env` prop, there are variables Alchemy sets for |
| 9 | you, and there are **bindings**. A binding is a capability an Effect |
| 10 | program requests. It attaches environment variables at deploy time, |
| 11 | and on some platforms it also attaches cloud permissions. |
| 12 | |
| 13 | ## Set environment variables |
| 14 | |
| 15 | ```typescript |
| 16 | const api = yield* Kubernetes.Deployment("Api", { |
| 17 | cluster, |
| 18 | image: "ghcr.io/acme/api:v3", |
| 19 | port: 8080, |
| 20 | env: { |
| 21 | LOG_LEVEL: "info", |
| 22 | FEATURES: { search: true, beta: false }, |
| 23 | BUCKET: bucket.bucketName, |
| 24 | }, |
| 25 | }); |
| 26 | ``` |
| 27 | |
| 28 | Each key becomes a container environment variable: |
| 29 | |
| 30 | - Strings are passed as they are. |
| 31 | - Other values are JSON-encoded. `FEATURES` above arrives as |
| 32 | `{"search":true,"beta":false}`. |
| 33 | - Outputs from other resources (`bucket.bucketName`) resolve at |
| 34 | deploy time and make the workload depend on that resource. |
| 35 | |
| 36 | A changed value re-applies the workload. A Deployment rolls its pods, |
| 37 | and a one-shot Job runs again. |
| 38 | |
| 39 | ## Variables Alchemy sets |
| 40 | |
| 41 | - `ALCHEMY_STACK_NAME` is the Stack's name. |
| 42 | - `ALCHEMY_STAGE` is the stage being deployed. |
| 43 | - `ALCHEMY_PHASE` is `runtime`. |
| 44 | - `PORT` is the Deployment's `port`. Only Deployments set it. |
| 45 | |
| 46 | Variables from `env` override these, and these override variables |
| 47 | from bindings. |
| 48 | |
| 49 | ## Keep secrets out of plain text |
| 50 | |
| 51 | Everything in `env` is written into the Deployment or Job object and |
| 52 | into Alchemy's state, where anyone who can read either can see it. |
| 53 | For credentials, store the value in a Kubernetes Secret and have the |
| 54 | workload read it from the cluster. A Secret is a |
| 55 | [Manifest](/kubernetes/objects/manifests) with `kind: "Secret"`: |
| 56 | |
| 57 | ```typescript |
| 58 | const dbSecret = yield* Kubernetes.Manifest("DbSecret", { |
| 59 | cluster, |
| 60 | manifest: { |
| 61 | apiVersion: "v1", |
| 62 | kind: "Secret", |
| 63 | metadata: { name: "db", namespace: "apps" }, |
| 64 | stringData: { url: databaseUrl }, |
| 65 | }, |
| 66 | }); |
| 67 | ``` |
| 68 | |
| 69 | Referencing a Secret from a container (`envFrom`, `valueFrom`, or a |
| 70 | volume mount) is a container-level field. `podTemplate` can add the |
| 71 | pod-level `volumes` entry, but not the container's `volumeMounts`, |
| 72 | because arrays in `podTemplate` replace the generated container list. |
| 73 | When a workload needs Secret references, declare the whole |
| 74 | Deployment as a Manifest. |
| 75 | |
| 76 | ## Bindings |
| 77 | |
| 78 | A binding is a capability a workload's Effect program asks for. It |
| 79 | returns a typed client for the program to call, and registers what |
| 80 | the workload needs at deploy time. Kubernetes workloads accept the |
| 81 | same binding contract as a Lambda function or an ECS task: |
| 82 | |
| 83 | - **`env`.** Environment variables. Every cluster supports these. |
| 84 | - **Cloud grants.** For example, AWS IAM `policyStatements`. These |
| 85 | need a cluster whose platform issues cloud credentials to pods. |
| 86 | |
| 87 | Bindings are declared inside the Effect implementation, so they |
| 88 | apply to workloads built from `main`. |
| 89 | |
| 90 | ### Bind values from other resources |
| 91 | |
| 92 | Inside an Effect program, yield a resource attribute to bind it. You |
| 93 | get back an Effect that returns the value at runtime: |
| 94 | |
| 95 | ```typescript |
| 96 | const worker = yield* Kubernetes.Job( |
| 97 | "Report", |
| 98 | { cluster, main: import.meta.url }, |
| 99 | Effect.gen(function* () { |
| 100 | const reportUrl = yield* api.url; |
| 101 | return { |
| 102 | run: Effect.gen(function* () { |
| 103 | const url = yield* reportUrl; |
| 104 | yield* Effect.log(`reporting to ${url}`); |
| 105 | }), |
| 106 | }; |
| 107 | }), |
| 108 | ); |
| 109 | ``` |
| 110 | |
| 111 | Alchemy resolves `api.url` at deploy time, delivers it to the pod, |
| 112 | and makes the Job depend on `api`. This works on every cluster, and |
| 113 | it works for derived values too, such as an `Output.interpolate` |
| 114 | string. Use the `env` prop for plain configuration, or for images |
| 115 | that don't run an Effect program. |
| 116 | |
| 117 | ### Cloud credentials |
| 118 | |
| 119 | Bindings to cloud services also grant permissions. One example is |
| 120 | `AWS.DynamoDB.PutItem(table)`. A cluster may have no way to issue |
| 121 | cloud credentials to pods. On such a cluster, a deploy that uses one |
| 122 | of these bindings fails with *bindings carry cloud credential grants |
| 123 | … but this cluster's platform has no workload-identity adapter*. You |
| 124 | have two options: |
| 125 | |
| 126 | - Run the workload on a cluster whose platform issues credentials. |
| 127 | On [Amazon EKS](/kubernetes/clusters/eks), grants become an IAM |
| 128 | role attached through EKS Pod Identity. |
| 129 | - Give the program credentials yourself, through a Kubernetes Secret |
| 130 | and your cloud SDK's standard environment variables. |
| 131 | |
| 132 | ## Where next |
| 133 | |
| 134 | - **[Jobs & CronJobs](/kubernetes/workloads/jobs).** Effect programs |
| 135 | that read configuration and run to completion. |
| 136 | - **[Bindings](/infrastructure-as-effects/binding).** How bindings |
| 137 | work across Alchemy. |