1---
2title: "Part 2: Namespaces & Configuration"
3description: Create a Namespace with Kubernetes.Manifest, move the Deployment into it, and configure the app with environment variables and resource limits.
4sidebar:
5 order: 2
6---
7
8import Terminal from "../../../../components/Terminal.astro";
9import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
10
11In [Part 1](/kubernetes/tutorial/part-1) you deployed podinfo into
12the `default` namespace. Now you'll give the app a namespace of its
13own, declared as a raw Kubernetes object. Then you'll configure the
14app from your program.
15
16## Declare a Namespace
17
18Anything that isn't a Deployment or a Job is a
19[`Kubernetes.Manifest`](/kubernetes/objects/manifests). A Manifest is
20a literal Kubernetes object, written exactly as you'd write it in
21YAML. Add a Namespace:
22
23```diff lang="typescript"
24Effect.gen(function* () {
25+ const namespace = yield* Kubernetes.Manifest("Namespace", {
26+ cluster,
27+ manifest: {
28+ apiVersion: "v1",
29+ kind: "Namespace",
30+ metadata: { name: "my-app" },
31+ },
32+ });
33
34 const web = yield* Kubernetes.Deployment("Web", {
35```
36
37Alchemy applies the object with server-side apply and tracks it like
38any other resource. Any kind works, such as `ConfigMap`, `Secret`,
39`StatefulSet`, `Ingress`, or a custom resource from a CRD.
40
41## Move the Deployment into it
42
43Point the Deployment at the new namespace:
44
45```diff lang="typescript"
46const web = yield* Kubernetes.Deployment("Web", {
47 cluster,
48 name: "web",
49+ namespace: namespace.name,
50 image: "ghcr.io/stefanprodan/podinfo:6.15.0",
51 port: 9898,
52 replicas: 2,
53 serviceType: "ClusterIP",
54});
55```
56
57`namespace.name` is an **Output**. It's a reference to the Manifest's
58attribute that resolves at deploy time. Passing it instead of the
59string `"my-app"` makes the Deployment depend on the Namespace, so
60Alchemy creates the Namespace first. Workload resources expect their
61namespace to exist already.
62
63## Configure the app
64
65podinfo shows the value of `PODINFO_UI_MESSAGE` in its response. Set
66it with `env`:
67
68```diff lang="typescript"
69const web = yield* Kubernetes.Deployment("Web", {
70 cluster,
71 name: "web",
72 namespace: namespace.name,
73 image: "ghcr.io/stefanprodan/podinfo:6.15.0",
74 port: 9898,
75 replicas: 2,
76 serviceType: "ClusterIP",
77+ env: {
78+ PODINFO_UI_MESSAGE: "hello from alchemy",
79+ },
80});
81```
82
83Every key in `env` becomes a container environment variable. Values
84can also be Outputs from other resources, such as a database URL or a
85bucket name. Alchemy resolves them before applying.
86
87## Set resource requests and limits
88
89Tell the scheduler what each pod needs:
90
91```diff lang="typescript"
92 env: {
93 PODINFO_UI_MESSAGE: "hello from alchemy",
94 },
95+ resources: {
96+ requests: { cpu: "10m", memory: "32Mi" },
97+ limits: { memory: "64Mi" },
98+ },
99});
100```
101
102`resources` maps directly onto the container's Kubernetes
103`resources` field and takes the usual Kubernetes quantities.
104
105## Deploy
106
107<Tabs syncKey="pkgManager">
108 <TabItem label="bun" icon="bun">
109 <Code code="bun alchemy deploy" lang="sh" />
110 </TabItem>
111 <TabItem label="npm" icon="npm">
112 <Code code="npm run alchemy deploy" lang="sh" />
113 </TabItem>
114 <TabItem label="pnpm" icon="pnpm">
115 <Code code="pnpm alchemy deploy" lang="sh" />
116 </TabItem>
117 <TabItem label="yarn" icon="seti:yarn">
118 <Code code="yarn alchemy deploy" lang="sh" />
119 </TabItem>
120</Tabs>
121
122<Terminal content={`[u]Plan[/u]: [g]1 to create[/g], [y]1 to update[/y]
123
124[g]+[/g] [b]Namespace[/b] [d](Kubernetes.Manifest)[/d]
125[y]~[/y] [b]Web[/b] [d](Kubernetes.Deployment)[/d]
126
127Proceed?
128◉ Yes ○ No
129[g]✓[/g] [b]Namespace[/b] [d](Kubernetes.Manifest)[/d] [g]created[/g]
130[g]✓[/g] [b]Web[/b] [d](Kubernetes.Deployment)[/d] [y]updated[/y]
131{
132[s2]context: "kind-alchemy",
133[s2]namespace: "my-app",
134[s2]service: "web",
135}`} />
136
137The Namespace is created first. The Deployment update applies its
138objects into `my-app` and deletes the old ones from `default`.
139Alchemy records every object it applies, and anything that drops out
140of the desired set is pruned.
141
142## Verify it worked
143
144The `default` namespace is empty again, and the app runs in `my-app`:
145
146```sh
147kubectl --context kind-alchemy get deployments --all-namespaces | grep web
148kubectl --context kind-alchemy -n my-app rollout status deployment/web
149kubectl --context kind-alchemy -n my-app port-forward service/web 9898:9898
150```
151
152```sh
153curl -s localhost:9898 | grep message
154```
155
156```json
157 "message": "hello from alchemy",
158```
159
160## Change it again
161
162Edit the message and deploy:
163
164```diff lang="typescript"
165env: {
166- PODINFO_UI_MESSAGE: "hello from alchemy",
167+ PODINFO_UI_MESSAGE: "configured in TypeScript",
168},
169```
170
171The plan shows one update. Alchemy re-applies the Deployment and
172Kubernetes rolls the pods over to the new environment, one replica
173at a time.
174
175## Recap
176
177You now have:
178
179- A `my-app` Namespace declared as a raw manifest
180- The Deployment moved into it, ordered after the Namespace by an
181 Output reference
182- Environment variables and resource limits set from your program
183
184In [Part 3](/kubernetes/tutorial/part-3), you'll run one-off and
185scheduled work with `Kubernetes.Job`.