1---
2title: Renaming Resources
3description: Migrate a resource's state across a logical ID change instead of replacing it.
4---
5
6Changing a [logical ID](/infrastructure-as-code/resource#logical-id)
7normally plans a
8[**replacement**](/infrastructure-as-code/resource-lifecycle#replace):
9a new resource is created under the new ID and the old one —
10including its physical resource, data, and
11attachments — is deleted. When you actually just renamed the
12resource, declare the rename with `Alchemy.renamedFrom` and alchemy
13migrates the state row instead:
14
15```diff lang="typescript"
16-const bucket = yield* Cloudflare.R2.Bucket("Bucket");
17+const bucket = yield* Cloudflare.R2.Bucket("Assets").pipe(
18+ Alchemy.renamedFrom("Bucket"),
19+);
20```
21
22The next deploy finds the state persisted under `Bucket`, moves it to
23`Assets`, and plans a single re-branding **update** — same
24[instance ID](/infrastructure-as-code/resource#physical-name), same
25physical name, nothing created or deleted in the cloud. The
26plan shows what's happening:
27
28```text
29Plan: 1 to update
30[Assets] update (renamed from Bucket)
31```
32
33Keep the decoration around; once no state row exists under the old
34ID it does nothing.
35
36## Namespaces
37
38A bare-string former id behaves exactly like the `id` argument: it
39resolves against the surrounding namespace (`Namespace.push`).
40Declared inside `Namespace.push("Site")`, `renamedFrom("Worker")`
41claims the former FQN `Site/Worker` — write the former id exactly
42as you would have written the `id` in the same position:
43
44```typescript
45// this Worker's FQN used to be `<id>/Worker`; now it is `<id>`
46// (what Cloudflare.Website.StaticSite does internally)
47yield* Worker(id, props).pipe(Alchemy.renamedFrom(`${id}/Worker`));
48```
49
50When a resource moved **between** namespaces, a relative id can't
51express the old location — pass the absolute form, the full FQN
52exactly as it appears in state:
53
54```typescript
55yield* Cloudflare.R2.Bucket("Assets").pipe(
56 Alchemy.renamedFrom({ fqn: "LegacySite/Assets" }),
57 Namespace.push("NewSite"),
58);
59```
60
61## Renamed more than once
62
63List every former id, **most recent first** — alchemy checks them in
64order and migrates from the first matching row:
65
66```typescript
67yield* Cloudflare.R2.Bucket("Assets").pipe(
68 Alchemy.renamedFrom("StaticAssets", "Bucket"),
69);
70```
71
72## Reusing the old ID
73
74The old ID can be reused by a **new** resource in the same deploy.
75The rename claim wins the state row — it's an explicit statement
76that the row was the renamed resource's — and the reusing resource
77is created fresh:
78
79```typescript
80// `Assets` keeps the physical resource previously known as
81// `Bucket`; this `Bucket` is a brand-new one.
82const assets = yield* Cloudflare.R2.Bucket("Assets").pipe(
83 Alchemy.renamedFrom("Bucket"),
84);
85const bucket = yield* Cloudflare.R2.Bucket("Bucket");
86```
87
88The migration deploy plans one **update** for the renamed resource
89(not a noop) — the reconcile re-brands the physical resource's
90ownership tags under the new logical ID, so the reused ID can never
91accidentally adopt the wrong cloud resource.
92
93## Safety semantics
94
95The migration is deliberately conservative.
96
97**Identity is proven, not assumed.** Only a migration ever copies an
98instance ID, so a leftover row from an interrupted move is provably
99a copy — it's dropped state-only, the physical resource untouched:
100
101```diff lang="typescript"
102-const bucket = yield* Cloudflare.R2.Bucket("Bucket");
103+// a crashed deploy left rows at BOTH ids with the SAME instance id;
104+// re-deploying drops the leftover copy — the cloud is never touched
105+const bucket = yield* Cloudflare.R2.Bucket("Assets").pipe(
106+ Alchemy.renamedFrom("Bucket"),
107+);
108```
109
110A former row with a *different* instance ID is someone else's — the
111old name was reused after the rename shipped — so the alias is
112ignored and the row follows normal lifecycle rules:
113
114```diff lang="typescript"
115 // deployed long after the rename: `Bucket` is its own resource now,
116 // with its own instance id — `renamedFrom` never adopts it
117 const assets = yield* Cloudflare.R2.Bucket("Assets").pipe(
118 Alchemy.renamedFrom("Bucket"),
119 );
120 const bucket = yield* Cloudflare.R2.Bucket("Bucket");
121```
122
123**Types must match.** A former row written by a different resource
124type can't be this resource's state, whatever its FQN says — nothing
125migrates, and the orphaned row follows its own lifecycle:
126
127```diff lang="typescript"
128-const cache = yield* Cloudflare.KV.Namespace("Store");
129+// the row at `Store` belongs to a KV.Namespace — it is never adopted;
130+// `Assets` is created fresh and `Store` is a normal orphan delete
131+const assets = yield* Cloudflare.R2.Bucket("Assets").pipe(
132+ Alchemy.renamedFrom("Store"),
133+);
134```
135
136And a different-typed row sitting at the *new* ID fails the plan —
137migrating over it would silently abandon its cloud resource:
138
139```diff lang="typescript"
140-const cache = yield* Cloudflare.KV.Namespace("Assets");
141-const bucket = yield* Cloudflare.R2.Bucket("Bucket");
142+// ✗ a state row of a different type already occupies 'Assets' —
143+// delete or rename the KV namespace first, then re-deploy
144+const bucket = yield* Cloudflare.R2.Bucket("Assets").pipe(
145+ Alchemy.renamedFrom("Bucket"),
146+);
147```
148
149**Ambiguity fails loudly.** Two resources claiming the same former
150ID fail instead of racing for the row:
151
152```typescript
153yield* Cloudflare.R2.Bucket("Assets").pipe(Alchemy.renamedFrom("Bucket"));
154yield* Cloudflare.R2.Bucket("Files").pipe(Alchemy.renamedFrom("Bucket"));
155// ✗ 'Assets' and 'Files' both claim former FQN 'Bucket'
156```
157
158**Shifts work.** Renames may pass through each other in one deploy —
159each row follows its resource, and pending replacement cleanup rides
160along:
161
162```typescript
163yield* Cloudflare.R2.Bucket("B").pipe(Alchemy.renamedFrom("A")); // row at A → B
164yield* Cloudflare.R2.Bucket("C").pipe(Alchemy.renamedFrom("B")); // row at B → C
165```
166
167**Swaps fail.** Exchanging two live IDs is a rename cycle — the two
168migrations would overwrite each other's rows — so the plan fails;
169rename through a temporary ID across two deploys instead:
170
171```typescript
172yield* Cloudflare.R2.Bucket("A").pipe(Alchemy.renamedFrom("B"));
173yield* Cloudflare.R2.Bucket("B").pipe(Alchemy.renamedFrom("A"));
174// ✗ rename cycle detected among ['A', 'B']
175```
176
177## Where next
178
179- [Providers](/infrastructure-as-code/provider) — the object that
180 implements a resource type's lifecycle. Next page.
181- [Resource Lifecycle](/infrastructure-as-code/resource-lifecycle) — the
182 replacement this decoration avoids.
183- [Resources](/infrastructure-as-code/resource) — logical IDs, instance
184 IDs, and physical names.