| 1 | --- |
| 2 | title: 2.0.0-beta.64 - Full AWS Coverage |
| 3 | date: 2026-07-22T06:00:00Z |
| 4 | category: release |
| 5 | excerpt: v2.0.0-beta.64 is the AWS release — generated coverage of (nearly) every AWS service, unified container platforms on ECS and EKS, Durable Lambda Functions, and Helm charts on EKS. Plus Python Workers, Drizzle for D1, GitHub Enterprise, and a new Effect-native test runner. |
| 6 | --- |
| 7 | |
| 8 | beta.64 is the AWS release. Alchemy now covers **206 AWS |
| 9 | services**, with every resource and binding live-tested against |
| 10 | the real cloud. It also adds first-class **containers on ECS and |
| 11 | EKS**, **Durable Lambda Functions**, and **Helm charts** on EKS. |
| 12 | |
| 13 | :::caution |
| 14 | **Breaking changes** |
| 15 | |
| 16 | - **Container props are restructured** |
| 17 | ([#867](https://github.com/alchemy-run/alchemy/pull/867)): |
| 18 | `docker: { base }` → `image`, `AWS.EKS.ServerHost` → |
| 19 | `AWS.EKS.Deployment`, `alchemy/Kubernetes` is removed in favor |
| 20 | of `AWS.EKS.Manifest`, and one-shot ECS Task impls return |
| 21 | `{ run }` instead of `{ fetch }`. |
| 22 | - **AWS prop data types are richer** |
| 23 | ([#797](https://github.com/alchemy-run/alchemy/pull/797)): |
| 24 | duration props take `Duration.Input` (`"1 hour"`, |
| 25 | `Duration.hours(1)`), and sensitive props (passwords, secrets, |
| 26 | keys) are `Redacted.Redacted<string>`. |
| 27 | ::: |
| 28 | |
| 29 | ## "100%" AWS coverage |
| 30 | |
| 31 | One PR ([#797](https://github.com/alchemy-run/alchemy/pull/797)) |
| 32 | took the AWS provider from a handful of services to all of them, |
| 33 | using the same [resource |
| 34 | factory](/blog/2026-07-02-cloudflare-resource-factory) that built |
| 35 | out the Cloudflare provider. Fleets of agents cataloged all 428 |
| 36 | generated SDK modules, then implemented and live-tested them in |
| 37 | waves: serverless first, then ECS/Fargate, EKS, EC2, and the long |
| 38 | tail. Everything left over is documented as out of scope |
| 39 | (deprecated APIs, data-only endpoints, billing objects). The |
| 40 | result: |
| 41 | |
| 42 | - **206 AWS services** with **683 resources** and **2,842 |
| 43 | bindings** (plus event sources and sinks), from DynamoDB and S3 |
| 44 | all the way out to MediaLive, IoT SiteWise, Bedrock, and |
| 45 | SageMaker. |
| 46 | - **2,692 live tests green by default** against real AWS. 375 |
| 47 | more are gated behind env vars because they are slow to |
| 48 | provision, expensive, or need special account entitlements. |
| 49 | - **Zero-orphan guarantee**: each round wiped the test account |
| 50 | clean, ran the full suite, then audited the account for |
| 51 | leftover resources. The loop ran until two consecutive rounds |
| 52 | left nothing behind. A passing test that leaks a cloud resource |
| 53 | is a provider bug, and the loop caught dozens of them. |
| 54 | |
| 55 | Bindings are 1:1 with IAM actions. Each binding your code uses |
| 56 | becomes a least-privilege policy statement on exactly the |
| 57 | resources it touches, whether the code runs on Lambda, ECS, or |
| 58 | EKS: |
| 59 | |
| 60 | ```typescript |
| 61 | const api = yield* AWS.ECS.Service( |
| 62 | "Api", |
| 63 | { cluster, main: import.meta.url, port: 3000, desiredCount: 2 }, |
| 64 | Effect.gen(function* () { |
| 65 | // init: each binding grants one IAM action on one ARN |
| 66 | const getItem = yield* AWS.DynamoDB.GetItem(table); |
| 67 | const send = yield* AWS.SQS.SendMessage(queue); |
| 68 | |
| 69 | return { |
| 70 | fetch: Effect.gen(function* () { |
| 71 | const item = yield* getItem({ Key: { pk: { S: "user#1" } } }); |
| 72 | yield* send({ MessageBody: JSON.stringify(item.Item) }); |
| 73 | return yield* HttpServerResponse.json({ ok: true }); |
| 74 | }), |
| 75 | }; |
| 76 | }).pipe( |
| 77 | Effect.provide([AWS.DynamoDB.GetItemHttp, AWS.SQS.SendMessageHttp]), |
| 78 | ), |
| 79 | ); |
| 80 | ``` |
| 81 | |
| 82 | You never write an IAM policy, and every error is typed — the |
| 83 | compiler tells you what can fail and you handle it like any other |
| 84 | Effect error. See [Bindings](/infrastructure-as-effects/binding) |
| 85 | for how it works. |
| 86 | |
| 87 | All of it is documented in the [API reference](/providers), with |
| 88 | usage examples on every resource and binding. |
| 89 | |
| 90 | ## ECS: Tasks and Services |
| 91 | |
| 92 | ECS gets the full platform treatment |
| 93 | ([#867](https://github.com/alchemy-run/alchemy/pull/867)): |
| 94 | [`Task`](/providers/aws/ecs/task) for run-to-completion work and |
| 95 | [`Service`](/providers/aws/ecs/service) for long-running servers. |
| 96 | Both are authored the same three ways as Workers and Lambda: an |
| 97 | external image, an inline effect, or a tagged class. |
| 98 | |
| 99 | Every container platform takes one image source, flat on props: |
| 100 | |
| 101 | ```typescript |
| 102 | { main } // bundle this Effect program |
| 103 | { main, image } // bundle FROM your base image |
| 104 | { context, dockerfile? } // build your own Dockerfile |
| 105 | { dockerfile: Dockerfile.inline`...` } // inline Dockerfile content |
| 106 | { image } // registry ref, mirrored into ECR |
| 107 | ``` |
| 108 | |
| 109 | The simplest `Service` runs a registry image behind a provisioned |
| 110 | Application Load Balancer: |
| 111 | |
| 112 | ```typescript |
| 113 | const cluster = yield* AWS.ECS.Cluster("AppCluster", {}); |
| 114 | |
| 115 | const nginx = yield* AWS.ECS.Service("Edge", { |
| 116 | cluster, |
| 117 | image: "public.ecr.aws/nginx/nginx:1.27", |
| 118 | port: 80, |
| 119 | desiredCount: 2, |
| 120 | loadBalancer: true, |
| 121 | }); |
| 122 | |
| 123 | return { url: nginx.url }; // http://<alb-dns-name> |
| 124 | ``` |
| 125 | |
| 126 | A `Service` can also run an Effect program. The impl returns |
| 127 | `{ fetch }`, and bindings grant IAM to the task role: |
| 128 | |
| 129 | ```typescript |
| 130 | const api = yield* AWS.ECS.Service( |
| 131 | "Api", |
| 132 | { cluster, main: import.meta.url, port: 3000, desiredCount: 2 }, |
| 133 | Effect.gen(function* () { |
| 134 | const putItem = yield* AWS.DynamoDB.PutItem(table); |
| 135 | return { |
| 136 | fetch: Effect.gen(function* () { |
| 137 | const request = yield* HttpServerRequest; |
| 138 | const order = (yield* request.json) as { orderId: string }; |
| 139 | yield* putItem({ Item: { pk: { S: order.orderId } } }); |
| 140 | return yield* HttpServerResponse.json({ ok: true }); |
| 141 | }), |
| 142 | }; |
| 143 | }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)), |
| 144 | ); |
| 145 | ``` |
| 146 | |
| 147 | A one-shot `Task` is the run-to-completion counterpart: the impl |
| 148 | returns `{ run }`, and the container exits when the effect |
| 149 | completes. Here it composes `main` with `Dockerfile.inline` — the |
| 150 | inline content builds the environment, and the bundled program is |
| 151 | layered on top: |
| 152 | |
| 153 | ```typescript |
| 154 | import { Dockerfile } from "alchemy/Docker"; |
| 155 | |
| 156 | const thumbnailer = yield* AWS.ECS.Task( |
| 157 | "Thumbnailer", |
| 158 | { |
| 159 | main: import.meta.url, |
| 160 | dockerfile: Dockerfile.inline` |
| 161 | FROM oven/bun:1 |
| 162 | RUN apt-get update && apt-get install -y ffmpeg |
| 163 | `, |
| 164 | cpu: 1024, |
| 165 | memory: 2048, |
| 166 | }, |
| 167 | Effect.gen(function* () { |
| 168 | const getObject = yield* AWS.S3.GetObject(bucket); |
| 169 | return { |
| 170 | run: Effect.gen(function* () { |
| 171 | // pull the video from S3, shell out to ffmpeg, then exit |
| 172 | }), |
| 173 | }; |
| 174 | }).pipe(Effect.provide(AWS.S3.GetObjectHttp)), |
| 175 | ); |
| 176 | ``` |
| 177 | |
| 178 | Tasks are launched from other compute with the |
| 179 | [`RunTask`](/providers/aws/ecs/runtask) binding, which grants |
| 180 | `ecs:RunTask` and `iam:PassRole` automatically — here a Lambda |
| 181 | function launches a task per request: |
| 182 | |
| 183 | ```typescript |
| 184 | const api = yield* AWS.Lambda.Function( |
| 185 | "Api", |
| 186 | { main: import.meta.url, url: true }, |
| 187 | Effect.gen(function* () { |
| 188 | const runTask = yield* AWS.ECS.RunTask(cluster, thumbnailer); |
| 189 | |
| 190 | return { |
| 191 | fetch: Effect.gen(function* () { |
| 192 | const response = yield* runTask({ |
| 193 | launchType: "FARGATE", |
| 194 | networkConfiguration: { |
| 195 | awsvpcConfiguration: { subnets: [subnetId] }, |
| 196 | }, |
| 197 | }); |
| 198 | return yield* HttpServerResponse.json({ |
| 199 | taskArn: response.tasks?.[0]?.taskArn, |
| 200 | }); |
| 201 | }), |
| 202 | }; |
| 203 | }), |
| 204 | ); |
| 205 | ``` |
| 206 | |
| 207 | Or run a task on a schedule with `AWS.ECS.every`, which |
| 208 | provisions the EventBridge rule and invoke role: |
| 209 | |
| 210 | ```typescript |
| 211 | yield* AWS.ECS.every("NightlyJob", "cron(0 3 * * ? *)", { |
| 212 | cluster, |
| 213 | task: nightlyTask, |
| 214 | subnets: [privateSubnet.subnetId], |
| 215 | }); |
| 216 | ``` |
| 217 | |
| 218 | Services go well past `loadBalancer: true`: multiple services can |
| 219 | share one load balancer, and props cover custom domains, |
| 220 | auto-scaling on CPU or request count, Fargate Spot capacity, EFS |
| 221 | volumes, secrets, and health checks. Docs: |
| 222 | [ECS](/aws/compute/ecs). |
| 223 | |
| 224 | ## EKS: Deployments, Jobs, Manifests, and Helm |
| 225 | |
| 226 | Kubernetes workloads live in the same TypeScript program as the |
| 227 | cluster itself — no YAML, no `kubectl`. `compute: "auto"` stands |
| 228 | up an EKS Auto Mode cluster where AWS manages the nodes: |
| 229 | |
| 230 | ```typescript |
| 231 | const network = yield* AWS.EC2.Network("Network", { |
| 232 | cidrBlock: "10.42.0.0/16", |
| 233 | availabilityZones: 2, |
| 234 | nat: "single", |
| 235 | }); |
| 236 | |
| 237 | const cluster = yield* AWS.EKS.Cluster("Cluster", { |
| 238 | compute: "auto", |
| 239 | resourcesVpcConfig: { subnetIds: network.privateSubnetIds }, |
| 240 | }); |
| 241 | |
| 242 | const api = yield* AWS.EKS.Deployment( |
| 243 | "Api", |
| 244 | { cluster, main: import.meta.url, port: 3000, replicas: 2, serviceType: "LoadBalancer" }, |
| 245 | Effect.gen(function* () { |
| 246 | const putItem = yield* AWS.DynamoDB.PutItem(table); // → pod-identity IAM |
| 247 | return { fetch: Effect.gen(function* () { /* ... */ }) }; |
| 248 | }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)), |
| 249 | ); |
| 250 | ``` |
| 251 | |
| 252 | [`Deployment`](/providers/kubernetes/deployment) synthesizes a |
| 253 | Kubernetes `Deployment`, `Service`, and `ServiceAccount`, with |
| 254 | bindings landing as pod-identity IAM. |
| 255 | |
| 256 | [`Job`](/providers/kubernetes/job) is the run-to-completion |
| 257 | counterpart — same image sources, and the impl returns `{ run }`: |
| 258 | |
| 259 | ```typescript |
| 260 | const seed = yield* AWS.EKS.Job( |
| 261 | "SeedData", |
| 262 | { cluster, main: import.meta.url }, |
| 263 | Effect.gen(function* () { |
| 264 | const putItem = yield* AWS.DynamoDB.PutItem(table); |
| 265 | return { |
| 266 | run: Effect.gen(function* () { |
| 267 | // runs to completion, then the pod exits |
| 268 | }), |
| 269 | }; |
| 270 | }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)), |
| 271 | ); |
| 272 | ``` |
| 273 | |
| 274 | Add `schedule` and a Job becomes a Kubernetes CronJob — here in |
| 275 | the external form, running a pre-built image: |
| 276 | |
| 277 | ```typescript |
| 278 | const nightly = yield* AWS.EKS.Job("NightlyBackfill", { |
| 279 | cluster, |
| 280 | image: "ghcr.io/acme/backfill:v3", |
| 281 | schedule: "0 3 * * *", |
| 282 | }); |
| 283 | ``` |
| 284 | |
| 285 | [`Manifest`](/providers/kubernetes/manifest) applies any raw |
| 286 | Kubernetes object via server-side apply, CRDs included — the |
| 287 | manifest is a literal object, exactly what you'd write in YAML. |
| 288 | No kubeconfig required: |
| 289 | |
| 290 | ```typescript |
| 291 | const sts = yield* AWS.EKS.Manifest("Cache", { |
| 292 | cluster, |
| 293 | manifest: { |
| 294 | apiVersion: "apps/v1", |
| 295 | kind: "StatefulSet", |
| 296 | metadata: { name: "cache", namespace: "apps" }, |
| 297 | spec: { replicas: 3 /* ... */ }, |
| 298 | }, |
| 299 | }); |
| 300 | ``` |
| 301 | |
| 302 | [`HelmChart`](/providers/kubernetes/helmchart) installs Helm charts |
| 303 | ([#891](https://github.com/alchemy-run/alchemy/pull/891)). It |
| 304 | renders the chart locally with `helm template` and applies the |
| 305 | objects to the cluster, so Alchemy owns the lifecycle: drift is |
| 306 | corrected on every deploy, dropped objects are pruned, and |
| 307 | destroy deletes everything: |
| 308 | |
| 309 | ```typescript |
| 310 | const ingress = yield* AWS.EKS.HelmChart("IngressNginx", { |
| 311 | cluster, |
| 312 | chart: "ingress-nginx", |
| 313 | repo: "https://kubernetes.github.io/ingress-nginx", |
| 314 | version: "4.11.2", |
| 315 | namespace: "ingress-nginx", |
| 316 | createNamespace: true, |
| 317 | values: { controller: { replicaCount: 2 } }, |
| 318 | }); |
| 319 | ``` |
| 320 | |
| 321 | Docs: [EKS](/aws/compute/eks). |
| 322 | |
| 323 | ## Durable Lambda Functions |
| 324 | |
| 325 | `AWS.Lambda.DurableFunction` writes long-running orchestrations |
| 326 | as plain code, built on AWS Lambda's durable execution model |
| 327 | ([#797](https://github.com/alchemy-run/alchemy/pull/797)). It |
| 328 | **is** a Lambda Function, with the same props and bindings, but |
| 329 | its body is checkpointed. `Durable.step` records each result so |
| 330 | it isn't re-run on replay. `Durable.sleep` suspends the execution |
| 331 | entirely; Lambda re-invokes the function to resume it. And |
| 332 | `Durable.waitForCallback` pauses on an external signal for up to |
| 333 | a year: |
| 334 | |
| 335 | ```typescript |
| 336 | export class OrderFlow extends AWS.Lambda.DurableFunction<OrderFlow>()( |
| 337 | "OrderFlow", |
| 338 | ) {} |
| 339 | |
| 340 | export default OrderFlow.make( |
| 341 | { main: import.meta.url, executionTimeout: "1 hour" }, |
| 342 | Effect.gen(function* () { |
| 343 | // init: resolve typed binding clients (IAM lands on this role) |
| 344 | const putItem = yield* AWS.DynamoDB.PutItem(table); |
| 345 | |
| 346 | return Effect.fn(function* (input: { orderId: string }) { |
| 347 | const reserved = yield* AWS.Lambda.Durable.step( |
| 348 | "reserve", |
| 349 | putItem({ Item: { pk: { S: input.orderId } } }).pipe(Effect.orDie), |
| 350 | { retry: { limit: 3, delay: "5 seconds" } }, |
| 351 | ); |
| 352 | yield* AWS.Lambda.Durable.sleep("cooldown", "10 minutes"); |
| 353 | return { orderId: input.orderId, reserved }; |
| 354 | }); |
| 355 | }).pipe(Effect.provide(AWS.DynamoDB.PutItemHttp)), |
| 356 | ); |
| 357 | ``` |
| 358 | |
| 359 | Replay requires the body to be deterministic, and the type system |
| 360 | enforces it: the run body has no access to cloud credentials, so |
| 361 | the only way to do I/O is through a binding client resolved in |
| 362 | init and called inside a `Durable.step`. |
| 363 | |
| 364 | `yield* OrderFlow` returns a typed handle to start, inspect, and |
| 365 | stop executions, and to complete callbacks from the outside: |
| 366 | |
| 367 | ```typescript |
| 368 | const orders = yield* OrderFlow; |
| 369 | const ref = yield* orders.start({ |
| 370 | name: "order-123", // idempotent start |
| 371 | params: { orderId: "123" }, |
| 372 | }); |
| 373 | const execution = yield* orders.get(ref.executionArn!); |
| 374 | // execution.Status: "RUNNING" | "SUCCEEDED" | "FAILED" | ... |
| 375 | ``` |
| 376 | |
| 377 | The vocabulary mirrors Cloudflare |
| 378 | [Workflows](/cloudflare/compute/workflows), so orchestration code |
| 379 | reads the same on both clouds. Reference: |
| 380 | [DurableFunction](/providers/aws/lambda/durablefunction). |
| 381 | |
| 382 | ## Effect SQL and Drizzle for D1 |
| 383 | |
| 384 | Drizzle now works end to end on Cloudflare D1 |
| 385 | ([#887](https://github.com/alchemy-run/alchemy/pull/887)), |
| 386 | mirroring the Neon and PlanetScale integrations. At deploy time, |
| 387 | `Drizzle.Schema` diffs your schema module and writes pending |
| 388 | migration SQL, and passing `schema.out` as the database's |
| 389 | `migrationsDir` makes the database apply it — generate first, |
| 390 | apply second, in one deploy: |
| 391 | |
| 392 | ```typescript |
| 393 | // src/db.ts |
| 394 | export const Database = Effect.gen(function* () { |
| 395 | const schema = yield* Drizzle.Schema("app-schema", { |
| 396 | schema: "./src/schema.ts", |
| 397 | out: "./migrations", |
| 398 | dialect: "sqlite", |
| 399 | }); |
| 400 | |
| 401 | return yield* Cloudflare.D1.Database("app-db", { |
| 402 | migrationsDir: schema.out, |
| 403 | }); |
| 404 | }); |
| 405 | ``` |
| 406 | |
| 407 | At runtime, a Worker binds the database with |
| 408 | `Cloudflare.D1.QueryDatabase` and hands the client to |
| 409 | `Drizzle.D1`. Every query is an Effect with `SqlError` in the |
| 410 | typed error channel: |
| 411 | |
| 412 | ```typescript |
| 413 | // src/api.ts |
| 414 | export default class Api extends Cloudflare.Worker<Api>()( |
| 415 | "Api", |
| 416 | { main: import.meta.url }, |
| 417 | Effect.gen(function* () { |
| 418 | const database = yield* Database; |
| 419 | const d1 = yield* Cloudflare.D1.QueryDatabase(database); |
| 420 | const db = yield* Drizzle.D1(d1, { relations }); |
| 421 | |
| 422 | return { |
| 423 | fetch: Effect.gen(function* () { |
| 424 | const users = yield* db.query.Users.findMany({ |
| 425 | with: { posts: true }, |
| 426 | }); |
| 427 | return yield* HttpServerResponse.json({ users }); |
| 428 | }), |
| 429 | }; |
| 430 | }).pipe(Effect.provide(Cloudflare.D1.QueryDatabaseBinding)), |
| 431 | ) {} |
| 432 | ``` |
| 433 | |
| 434 | The low-level effect-sql clients also get one home: the new |
| 435 | `alchemy/SQL/*` subpaths, with a raw tagged-template client for |
| 436 | each Drizzle integration. It binds inside a Worker the same way: |
| 437 | |
| 438 | ```typescript |
| 439 | import * as SQL from "alchemy/SQL/D1"; |
| 440 | |
| 441 | Effect.gen(function* () { |
| 442 | const d1 = yield* Cloudflare.D1.QueryDatabase(database); |
| 443 | const sql = yield* SQL.D1(d1); |
| 444 | |
| 445 | return { |
| 446 | fetch: Effect.gen(function* () { |
| 447 | const rows = yield* sql`SELECT id, name FROM users`; |
| 448 | return yield* HttpServerResponse.json({ rows }); |
| 449 | }), |
| 450 | }; |
| 451 | }).pipe(Effect.provide(Cloudflare.D1.QueryDatabaseBinding)); |
| 452 | ``` |
| 453 | |
| 454 | All clients share the same lifecycle: nothing connects at plan or |
| 455 | deploy time, the client is built lazily on the first query, and |
| 456 | it's torn down when the request settles. The website's new |
| 457 | [SQL hub](/sql) collects effect-sql, Drizzle, and migrations in |
| 458 | one place |
| 459 | ([#903](https://github.com/alchemy-run/alchemy/pull/903)). |
| 460 | |
| 461 | ## Also in this release |
| 462 | |
| 463 | - **Python Workers** |
| 464 | ([#861](https://github.com/alchemy-run/alchemy/pull/861)) — |
| 465 | point `main` at a `.py` file and `Cloudflare.Worker` deploys a |
| 466 | Python Worker, with dependencies vendored from `pyproject.toml` |
| 467 | via uv. Docs: |
| 468 | [Python Workers](/cloudflare/compute/python-workers). |
| 469 | - **Workers AI binding** |
| 470 | ([#850](https://github.com/alchemy-run/alchemy/pull/850)) — |
| 471 | `Cloudflare.Workers.AI` runs models on Cloudflare's GPU fleet |
| 472 | from a Worker. |
| 473 | - **Durable Objects pin to a region** |
| 474 | ([#851](https://github.com/alchemy-run/alchemy/pull/851)) with |
| 475 | `locationHint`, and DO classes **transfer between workers** |
| 476 | declaratively |
| 477 | ([#803](https://github.com/alchemy-run/alchemy/pull/803)). |
| 478 | - **GitHub Enterprise + deployment environments** |
| 479 | ([#845](https://github.com/alchemy-run/alchemy/pull/845)) — |
| 480 | `alchemy login` accepts an enterprise host (GHES and data |
| 481 | residency), and the new `GitHub.Environment` resource manages |
| 482 | deployment environments. |
| 483 | - **Effect-native test runner** |
| 484 | ([#848](https://github.com/alchemy-run/alchemy/pull/848)) — |
| 485 | the whole alchemy test suite now runs in a single bun process |
| 486 | on `alchemy-test`, with output designed for agents. Full story: |
| 487 | [A Test Runner Built for |
| 488 | Agents](/blog/2026-07-22-a-test-runner-for-agents). The live |
| 489 | suite converged to green: 4,458 passed |
| 490 | ([#916](https://github.com/alchemy-run/alchemy/pull/916)). |
| 491 | - **RDS safety**: `MasterUserPassword` is fingerprint-guarded so |
| 492 | it's no longer re-sent on every reconcile |
| 493 | ([#876](https://github.com/alchemy-run/alchemy/pull/876)), and |
| 494 | `skipFinalSnapshot: false` takes a final snapshot on deliberate |
| 495 | teardown |
| 496 | ([#877](https://github.com/alchemy-run/alchemy/pull/877)). |
| 497 | - **`AWS_REGION` overrides the SSO profile's stored region** |
| 498 | ([#895](https://github.com/alchemy-run/alchemy/pull/895)). |
| 499 | - **Engine reliability** — destroy aggregates GC failures and |
| 500 | refuses to silently orphan zombie rows |
| 501 | ([#899](https://github.com/alchemy-run/alchemy/pull/899), |
| 502 | [#862](https://github.com/alchemy-run/alchemy/pull/862)), |
| 503 | `--force` refreshes stale bound attributes |
| 504 | ([#900](https://github.com/alchemy-run/alchemy/pull/900)), |
| 505 | binding diffs are stable across runs |
| 506 | ([#898](https://github.com/alchemy-run/alchemy/pull/898)), |
| 507 | unresolved Outputs are never persisted into state |
| 508 | ([#835](https://github.com/alchemy-run/alchemy/pull/835)), and |
| 509 | truncated physical names stay unique |
| 510 | ([#868](https://github.com/alchemy-run/alchemy/pull/868)). |
| 511 | - **Actions get local bindings and Output accessors** |
| 512 | ([#807](https://github.com/alchemy-run/alchemy/pull/807)), and |
| 513 | `alchemy login` works without an `alchemy.run.ts` |
| 514 | ([#842](https://github.com/alchemy-run/alchemy/pull/842)). |
| 515 | - **Migrating from v1** now adopts existing resources instead of |
| 516 | destroying and recreating them |
| 517 | ([#819](https://github.com/alchemy-run/alchemy/pull/819)) — |
| 518 | see [Migrating from v1](/migrating-from-v1). |
| 519 | - **Cloudflare fixes**: queue-consumer wiring surviving mid-start |
| 520 | reconciles |
| 521 | ([#917](https://github.com/alchemy-run/alchemy/pull/917)), |
| 522 | asset-upload retries |
| 523 | ([#914](https://github.com/alchemy-run/alchemy/pull/914)), |
| 524 | wasm in `alchemy dev` |
| 525 | ([#881](https://github.com/alchemy-run/alchemy/pull/881)), and |
| 526 | Effect-valued env bindings converging in plans |
| 527 | ([#890](https://github.com/alchemy-run/alchemy/pull/890)). |
| 528 | - **The website now lives at |
| 529 | [alchemy.run](https://alchemy.run)** |
| 530 | ([#806](https://github.com/alchemy-run/alchemy/pull/806)), |
| 531 | builds 8x faster on Astro 7 |
| 532 | ([#873](https://github.com/alchemy-run/alchemy/pull/873)), and |
| 533 | the [Layers](/infrastructure-as-effects/layers), |
| 534 | [Bindings](/infrastructure-as-effects/binding), and |
| 535 | [Runtime](/infrastructure-as-effects/runtime) |
| 536 | concept pages are rewritten. |
| 537 | |
| 538 | ## Where to go next |
| 539 | |
| 540 | - [AWS](/aws) — the provider hub: setup, tutorial, and guides |
| 541 | - [ECS](/aws/compute/ecs) and [EKS](/aws/compute/eks) |
| 542 | - [Choosing a runtime](/aws/compute/choosing-a-runtime) |
| 543 | - [SQL](/sql) |
| 544 | - [Bindings](/infrastructure-as-effects/binding) |
| 545 | - [CHANGELOG](https://github.com/alchemy-run/alchemy/blob/main/CHANGELOG.md#v200-beta64) |
| 546 | - [Compare v2.0.0-beta.63 → v2.0.0-beta.64](https://github.com/alchemy-run/alchemy/compare/v2.0.0-beta.63...v2.0.0-beta.64) |