| 1 | --- |
| 2 | title: Manifests |
| 3 | description: Apply any single Kubernetes object with Kubernetes.Manifest, including Namespaces, ConfigMaps, Secrets, StatefulSets, Ingresses, CRDs and custom resources, and order objects with Output references. |
| 4 | --- |
| 5 | |
| 6 | [`Kubernetes.Manifest`](/providers/kubernetes/manifest) applies one |
| 7 | Kubernetes object, written as a literal object in the same shape as |
| 8 | its YAML. Use it for everything |
| 9 | [`Deployment`](/kubernetes/workloads/deployments) and |
| 10 | [`Job`](/kubernetes/workloads/jobs) don't model. That includes |
| 11 | Namespaces, ConfigMaps, Secrets, StatefulSets, DaemonSets, Ingresses, |
| 12 | RBAC, NetworkPolicies, and custom resources. |
| 13 | |
| 14 | ```typescript |
| 15 | const config = yield* Kubernetes.Manifest("AppConfig", { |
| 16 | cluster, |
| 17 | manifest: { |
| 18 | apiVersion: "v1", |
| 19 | kind: "ConfigMap", |
| 20 | metadata: { name: "app-config", namespace: "apps" }, |
| 21 | data: { LOG_LEVEL: "info" }, |
| 22 | }, |
| 23 | }); |
| 24 | ``` |
| 25 | |
| 26 | The object is applied with server-side apply, re-applied whenever |
| 27 | the manifest changes, and deleted on destroy. |
| 28 | |
| 29 | ## Required fields |
| 30 | |
| 31 | - `apiVersion` and `kind` are required, as in YAML. |
| 32 | - `metadata.name` is required. Objects that rely on `generateName` |
| 33 | aren't supported, because Alchemy needs a fixed name to find the |
| 34 | object again. |
| 35 | - `metadata.namespace` is required for namespaced kinds. A Manifest |
| 36 | doesn't default to `default`. Omit the namespace only for |
| 37 | cluster-scoped kinds such as `Namespace`, `ClusterRole`, or |
| 38 | `CustomResourceDefinition`. |
| 39 | |
| 40 | Every other field (`spec`, `data`, `stringData`, `type`, `rules`, …) |
| 41 | is passed through untouched. The Kubernetes API server validates the |
| 42 | object when it's applied, and a rejected object fails the deploy |
| 43 | with the server's message. |
| 44 | |
| 45 | ## Order objects with Outputs |
| 46 | |
| 47 | Each Manifest is its own resource, and resources deploy in parallel |
| 48 | unless one references another. To create an ordering, reference the |
| 49 | `name`, `namespace`, `kind`, `apiVersion`, or `uid` attribute of |
| 50 | another Manifest: |
| 51 | |
| 52 | ```typescript |
| 53 | const ns = yield* Kubernetes.Manifest("Namespace", { |
| 54 | cluster, |
| 55 | manifest: { apiVersion: "v1", kind: "Namespace", metadata: { name: "apps" } }, |
| 56 | }); |
| 57 | |
| 58 | const quota = yield* Kubernetes.Manifest("Quota", { |
| 59 | cluster, |
| 60 | manifest: { |
| 61 | apiVersion: "v1", |
| 62 | kind: "ResourceQuota", |
| 63 | metadata: { name: "apps-quota", namespace: ns.name }, |
| 64 | spec: { hard: { pods: "50", "requests.cpu": "20" } }, |
| 65 | }, |
| 66 | }); |
| 67 | ``` |
| 68 | |
| 69 | `ns.name` resolves to `"apps"`. It also tells Alchemy to create the |
| 70 | Namespace before the ResourceQuota, and to delete the ResourceQuota |
| 71 | before the Namespace on destroy. |
| 72 | |
| 73 | ## Run a StatefulSet |
| 74 | |
| 75 | Sometimes you need full control of a workload's containers, such as |
| 76 | probes, volume mounts, sidecars, or Secret references. In that case, |
| 77 | write the workload as a Manifest: |
| 78 | |
| 79 | ```typescript |
| 80 | const redis = yield* Kubernetes.Manifest("Redis", { |
| 81 | cluster, |
| 82 | manifest: { |
| 83 | apiVersion: "apps/v1", |
| 84 | kind: "StatefulSet", |
| 85 | metadata: { name: "redis", namespace: ns.name }, |
| 86 | spec: { |
| 87 | serviceName: "redis", |
| 88 | replicas: 1, |
| 89 | selector: { matchLabels: { app: "redis" } }, |
| 90 | template: { |
| 91 | metadata: { labels: { app: "redis" } }, |
| 92 | spec: { |
| 93 | containers: [ |
| 94 | { |
| 95 | name: "redis", |
| 96 | image: "redis:7.4", |
| 97 | ports: [{ containerPort: 6379 }], |
| 98 | volumeMounts: [{ name: "data", mountPath: "/data" }], |
| 99 | }, |
| 100 | ], |
| 101 | }, |
| 102 | }, |
| 103 | volumeClaimTemplates: [ |
| 104 | { |
| 105 | metadata: { name: "data" }, |
| 106 | spec: { |
| 107 | accessModes: ["ReadWriteOnce"], |
| 108 | resources: { requests: { storage: "1Gi" } }, |
| 109 | }, |
| 110 | }, |
| 111 | ], |
| 112 | }, |
| 113 | }, |
| 114 | }); |
| 115 | ``` |
| 116 | |
| 117 | A Manifest applies exactly one object. The StatefulSet's headless |
| 118 | Service is a second Manifest. |
| 119 | |
| 120 | ## Custom resources |
| 121 | |
| 122 | Kinds Alchemy doesn't know are looked up through the cluster's API |
| 123 | discovery endpoint, so custom resources work without any |
| 124 | registration. The CRD must be installed before the first custom |
| 125 | resource is applied. When the CRD comes from the same Stack, |
| 126 | reference one of its attributes from the custom resource. An |
| 127 | annotation is a convenient place for it: |
| 128 | |
| 129 | ```typescript |
| 130 | const crd = yield* Kubernetes.Manifest("WidgetCrd", { |
| 131 | cluster, |
| 132 | manifest: { |
| 133 | apiVersion: "apiextensions.k8s.io/v1", |
| 134 | kind: "CustomResourceDefinition", |
| 135 | metadata: { name: "widgets.acme.io" }, |
| 136 | spec: { |
| 137 | group: "acme.io", |
| 138 | scope: "Namespaced", |
| 139 | names: { plural: "widgets", singular: "widget", kind: "Widget" }, |
| 140 | versions: [ |
| 141 | { |
| 142 | name: "v1", |
| 143 | served: true, |
| 144 | storage: true, |
| 145 | schema: { |
| 146 | openAPIV3Schema: { |
| 147 | type: "object", |
| 148 | properties: { |
| 149 | spec: { type: "object", properties: { size: { type: "integer" } } }, |
| 150 | }, |
| 151 | }, |
| 152 | }, |
| 153 | }, |
| 154 | ], |
| 155 | }, |
| 156 | }, |
| 157 | }); |
| 158 | |
| 159 | const widget = yield* Kubernetes.Manifest("Widget", { |
| 160 | cluster, |
| 161 | manifest: { |
| 162 | apiVersion: "acme.io/v1", |
| 163 | kind: "Widget", |
| 164 | metadata: { |
| 165 | name: "w", |
| 166 | namespace: "default", |
| 167 | annotations: { "acme.io/crd": crd.name }, |
| 168 | }, |
| 169 | spec: { size: 3 }, |
| 170 | }, |
| 171 | }); |
| 172 | ``` |
| 173 | |
| 174 | The reference also orders destroy, so the Widget is deleted before |
| 175 | its CRD. CRDs installed by an operator's Helm chart come from a |
| 176 | [HelmChart](/kubernetes/objects/helm-charts). Reference one of its |
| 177 | attributes the same way. |
| 178 | |
| 179 | ## Updates and replacement |
| 180 | |
| 181 | An object's identity is its cluster, `apiVersion`, `kind`, `name`, |
| 182 | and `namespace`: |
| 183 | |
| 184 | - **Any other field.** Changing `spec`, `data`, or labels re-applies |
| 185 | the object in place. |
| 186 | - **`apiVersion`, `kind`, `name`, or `namespace`.** Changing one |
| 187 | causes a replacement. The new object is applied, then the old one |
| 188 | is deleted. |
| 189 | - **`cluster`.** Changing it causes a replacement. See |
| 190 | [Move a resource to another cluster](/kubernetes/clusters/connecting#move-a-resource-to-another-cluster). |
| 191 | |
| 192 | Server-side apply sends the full object and takes ownership of every |
| 193 | field in it. Fields you remove from the manifest are removed from |
| 194 | the object. Fields that Alchemy never set are left alone, whether |
| 195 | controllers or other tools set them. See |
| 196 | [How objects are managed](/kubernetes/workloads/object-lifecycle). |
| 197 | |
| 198 | ## Attributes |
| 199 | |
| 200 | - `name` is the `metadata.name` of the object. |
| 201 | - `namespace` is the `metadata.namespace`, or `undefined` for |
| 202 | cluster-scoped kinds. |
| 203 | - `kind` is the object's kind. |
| 204 | - `apiVersion` is the object's API version. |
| 205 | - `uid` is the UID the API server assigned. |
| 206 | - `ref` is a reference to the object (`apiVersion`, `kind`, `name`, |
| 207 | `namespace`). |
| 208 | - `connection` is the cluster connection. |
| 209 | |
| 210 | ## Where next |
| 211 | |
| 212 | - **[Helm charts](/kubernetes/objects/helm-charts).** Apply many |
| 213 | objects from a chart as one resource. |
| 214 | - **[How objects are managed](/kubernetes/workloads/object-lifecycle).** |
| 215 | Field ownership, drift, and deletion. |
| 216 | - **[`Manifest` reference](/providers/kubernetes/manifest).** Every |
| 217 | prop and attribute. |