| 1 | --- |
| 2 | title: How objects are managed |
| 3 | description: How Alchemy applies, orders, prunes, and deletes Kubernetes objects, including server-side apply and field ownership, drift, replacement, unreachable clusters, and adoption. |
| 4 | --- |
| 5 | |
| 6 | Every Kubernetes resource in Alchemy turns into one or more |
| 7 | Kubernetes objects. That includes |
| 8 | [`Deployment`](/kubernetes/workloads/deployments), |
| 9 | [`Job`](/kubernetes/workloads/jobs), |
| 10 | [`Manifest`](/kubernetes/objects/manifests), and |
| 11 | [`HelmChart`](/kubernetes/objects/helm-charts). This page describes |
| 12 | what happens to those objects across deploys. |
| 13 | |
| 14 | - `Deployment` creates a ServiceAccount, a Deployment, and a Service. |
| 15 | - `Job` creates a ServiceAccount and a Job or CronJob. |
| 16 | - `Manifest` creates the one object in `manifest`. |
| 17 | - `HelmChart` creates every object in the render, plus the Namespace |
| 18 | when `createNamespace` is set. |
| 19 | |
| 20 | ## Server-side apply |
| 21 | |
| 22 | Alchemy writes objects with Kubernetes |
| 23 | [server-side apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) |
| 24 | under the field manager `alchemy`, with `force` enabled. Each apply |
| 25 | sends the complete desired object, and the API server merges it: |
| 26 | |
| 27 | - **Alchemy owns the fields it sends.** Forcing means a conflicting |
| 28 | value set by another manager is overwritten, and Alchemy becomes |
| 29 | that field's owner. Another manager might be `kubectl edit` or |
| 30 | another tool. |
| 31 | - **Fields Alchemy stops sending are removed.** The exception is a |
| 32 | field that another manager also set. |
| 33 | - **Fields Alchemy never sends are left alone.** Controllers can |
| 34 | keep writing `status`, autoscalers can manage fields Alchemy |
| 35 | doesn't set, and annotations added by other tools survive. |
| 36 | |
| 37 | This has one consequence for autoscaling. If a |
| 38 | HorizontalPodAutoscaler manages a Deployment's replicas, Alchemy's |
| 39 | `replicas` value (default `1`) still overwrites them on each apply. |
| 40 | Write the workload as a [Manifest](/kubernetes/objects/manifests) |
| 41 | without `spec.replicas` when an autoscaler should own the count. |
| 42 | |
| 43 | ## Apply and delete order |
| 44 | |
| 45 | Within one resource, Alchemy applies objects in dependency order. |
| 46 | Namespaces come first, then CustomResourceDefinitions, then |
| 47 | ServiceAccounts, ConfigMaps and Secrets, Services, workloads, and |
| 48 | finally any other kind. Deletes run in the reverse order. Objects at |
| 49 | the same level are applied in parallel. |
| 50 | |
| 51 | Across resources, the order comes from Output references. A |
| 52 | Deployment whose `namespace` is `ns.name` deploys after the `ns` |
| 53 | Manifest and is destroyed before it. Resources that don't reference |
| 54 | each other deploy in parallel. |
| 55 | |
| 56 | ## Pruning |
| 57 | |
| 58 | Alchemy records every object each resource applied. An update can |
| 59 | produce a different set. For example, a chart's new version drops a |
| 60 | template, a Job's spec changes its name, or a Deployment moves |
| 61 | namespace. Alchemy then deletes the objects that are no longer in the |
| 62 | set before applying the new ones. |
| 63 | |
| 64 | Deletes use background propagation, so a deleted Job takes its pods |
| 65 | with it and a deleted Deployment takes its ReplicaSets. |
| 66 | |
| 67 | ## When objects are applied |
| 68 | |
| 69 | Alchemy applies a resource's objects when the resource is created or |
| 70 | updated. A resource updates when its props change. It also updates |
| 71 | when the content of its image source changes. An image source is a |
| 72 | `context` directory, a bundled `main` program, or a local chart |
| 73 | directory. |
| 74 | |
| 75 | A deploy with no changes applies nothing. Edits made out of band, |
| 76 | such as `kubectl scale` or `kubectl edit`, stay in place until the |
| 77 | resource is next applied. To converge everything back to your |
| 78 | program, force an apply of every resource: |
| 79 | |
| 80 | ```sh |
| 81 | alchemy deploy --force |
| 82 | ``` |
| 83 | |
| 84 | [`alchemy drift`](/cli/drift) detects objects deleted out of band, |
| 85 | such as a Manifest's object or the ServiceAccount of a Deployment or |
| 86 | Job. It reports the resource as missing, and |
| 87 | `alchemy drift --repair` recreates it. It doesn't compare object |
| 88 | fields, and a HelmChart is only checked for a reachable cluster. |
| 89 | |
| 90 | ## Replacement |
| 91 | |
| 92 | A resource is replaced when its identity changes: |
| 93 | |
| 94 | - `Deployment` identity is its cluster and namespace. |
| 95 | - `Job` identity is its cluster and namespace. |
| 96 | - `Manifest` identity is its cluster, `apiVersion`, `kind`, `name`, |
| 97 | and `namespace`. |
| 98 | - `HelmChart` identity is its cluster, release name, and namespace. |
| 99 | |
| 100 | Replacement creates the new generation first, then deletes the old |
| 101 | one. Sometimes both generations contain an object with the same name |
| 102 | in the same place. This happens when the same cluster is reached |
| 103 | through a different connection, or when a chart object's name |
| 104 | ignores the release name. Deleting the old generation then removes |
| 105 | the object the new one just applied. Run `alchemy deploy --force` |
| 106 | after such a change. See |
| 107 | [Move a resource to another cluster](/kubernetes/clusters/connecting#move-a-resource-to-another-cluster) |
| 108 | and [Helm charts](/kubernetes/objects/helm-charts#release-name-and-namespace). |
| 109 | |
| 110 | ## Deploys don't wait for readiness |
| 111 | |
| 112 | Applying succeeds as soon as the API server accepts the objects. A |
| 113 | Deployment's pods may still be pulling images, and a Job may still |
| 114 | be running or failing. The one exception is a `LoadBalancer` Service, |
| 115 | whose deploy waits up to about three minutes for an address. Use |
| 116 | `kubectl rollout status`, a smoke-test |
| 117 | [Job](/kubernetes/workloads/jobs), or your own health checks to |
| 118 | gate on readiness. |
| 119 | |
| 120 | ## Transient errors |
| 121 | |
| 122 | Applies retry for about a minute on API server errors (5xx), |
| 123 | throttling (429), and authentication or authorization failures (401 |
| 124 | and 403). The 401 and 403 errors are common for a few seconds after |
| 125 | a hosted cluster or its access grants are created. Validation errors |
| 126 | such as 422 fail immediately with the API server's message. |
| 127 | |
| 128 | ## Destroy and unreachable clusters |
| 129 | |
| 130 | `alchemy destroy` connects to each resource's cluster using the |
| 131 | connection stored in state and deletes its objects. What happens |
| 132 | when the cluster can't be reached depends on why: |
| 133 | |
| 134 | - **The cluster is gone.** The cluster's platform reports that it |
| 135 | was deleted. Its objects went with it, so Alchemy treats them as |
| 136 | deleted. Alchemy still removes any cloud resources the platform |
| 137 | created for the workload. On EKS, those are the ECR repository and |
| 138 | the Pod Identity role. |
| 139 | - **The cluster is unreachable.** This covers a kubeconfig cluster |
| 140 | that's stopped, or a network problem. `Deployment` and `Job` skip |
| 141 | their in-cluster objects and still remove adapter-owned cloud |
| 142 | resources. `Manifest` and `HelmChart` fail so you can retry once |
| 143 | the cluster is back. |
| 144 | |
| 145 | Deleting an object that's already gone is not an error, so destroy |
| 146 | can be re-run safely. |
| 147 | |
| 148 | ## Adoption and listing |
| 149 | |
| 150 | Kubernetes objects carry no Alchemy ownership marker, so Alchemy |
| 151 | doesn't enumerate or adopt existing objects. Applying a Manifest or |
| 152 | chart whose objects already exist takes over their fields through |
| 153 | server-side apply. From then on, Alchemy manages and deletes them |
| 154 | like objects it created. |
| 155 | |
| 156 | ## Where next |
| 157 | |
| 158 | - **[Cluster adapters](/kubernetes/clusters/cluster-adapters).** The |
| 159 | platform layer behind `connect`, images, and identity. |
| 160 | - **[Resource lifecycle](/infrastructure-as-code/resource-lifecycle).** |
| 161 | How Alchemy plans creates, updates, and replacements across all |
| 162 | providers. |