| 1 | --- |
| 2 | title: 2.0.0-beta.65 - SageMaker HyperPod |
| 3 | date: 2026-07-27T06:00:00Z |
| 4 | category: release |
| 5 | excerpt: v2.0.0-beta.65 provisions SageMaker HyperPod clusters — Slurm or EKS orchestrated — with task governance and typed workloads. Plus Worker bundling fixes for Hono-style routes and Drizzle schemas, one-pass destroys for VPC-attached Lambdas, and a smarter nuke. |
| 6 | --- |
| 7 | |
| 8 | beta.65 brings **SageMaker HyperPod** to the AWS provider: |
| 9 | persistent ML training fleets, Slurm or EKS orchestrated, with |
| 10 | task governance and typed workloads. It also fixes two Worker |
| 11 | bundling bugs that broke deployed apps, and makes `destroy` and |
| 12 | `nuke` converge in one pass. |
| 13 | |
| 14 | :::caution |
| 15 | **Breaking change** |
| 16 | |
| 17 | The `alchemy/SQL` barrel is removed |
| 18 | ([#951](https://github.com/alchemy-run/alchemy/pull/951)). It |
| 19 | eagerly loaded every backend, so projects without the optional |
| 20 | `@effect/sql-pg` peer crashed on import. Import each client from |
| 21 | its own subpath instead: |
| 22 | |
| 23 | ```diff |
| 24 | -import * as SQL from "alchemy/SQL"; |
| 25 | +import * as SQL from "alchemy/SQL/D1"; |
| 26 | +import * as SQL from "alchemy/SQL/Postgres"; |
| 27 | ``` |
| 28 | |
| 29 | ::: |
| 30 | |
| 31 | ## SageMaker HyperPod |
| 32 | |
| 33 | HyperPod is AWS's persistent fleet for ML training and inference: |
| 34 | accelerated instances with automatic health checks and |
| 35 | faulty-node replacement, staying up between jobs. Where ECS and |
| 36 | EKS run application containers, HyperPod runs distributed |
| 37 | training ([#924](https://github.com/alchemy-run/alchemy/pull/924)). |
| 38 | |
| 39 | [`AWS.SageMaker.Cluster`](/providers/aws/sagemaker/cluster) |
| 40 | provisions the fleet. The default orchestrator is Slurm — an S3 |
| 41 | lifecycle script, an execution role, and instance groups: |
| 42 | |
| 43 | ```typescript |
| 44 | const cluster = yield* AWS.SageMaker.Cluster("TrainingCluster", { |
| 45 | instanceGroups: { |
| 46 | controller: { |
| 47 | InstanceType: "ml.t3.medium", |
| 48 | InstanceCount: 1, |
| 49 | ExecutionRole: role.roleArn, |
| 50 | LifeCycleConfig: { |
| 51 | SourceS3Uri: script.sourceS3Uri, |
| 52 | OnCreate: script.onCreate, |
| 53 | }, |
| 54 | }, |
| 55 | }, |
| 56 | }); |
| 57 | ``` |
| 58 | |
| 59 | Instance groups reconcile in place: change a count or add a group |
| 60 | and the cluster updates; remove one and it is deleted from the |
| 61 | cluster. Jobs are submitted with `sbatch` over SSM — every node |
| 62 | is an SSM target. |
| 63 | |
| 64 | Pass `orchestrator: { Eks }` and the fleet's nodes join an EKS |
| 65 | cluster you provide. From there, workloads are ordinary |
| 66 | Kubernetes objects — and |
| 67 | [`AWS.EKS.Job`](/providers/kubernetes/job) and |
| 68 | [`Deployment`](/providers/kubernetes/deployment) opt onto HyperPod |
| 69 | nodes with the `hyperpod` prop. The instance-group keys carry |
| 70 | through the cluster's attributes as types, so a typo'd group name |
| 71 | is a compile error: |
| 72 | |
| 73 | ```typescript |
| 74 | const train = yield* AWS.EKS.Job( |
| 75 | "Train", |
| 76 | { |
| 77 | cluster: eks, |
| 78 | main: import.meta.url, |
| 79 | hyperpod: { instanceGroup: hyperpod.instanceGroups.workers }, |
| 80 | }, |
| 81 | Effect.gen(function* () { |
| 82 | return { |
| 83 | run: Effect.gen(function* () { |
| 84 | // training logic; bindings land IAM on pod identity |
| 85 | }), |
| 86 | }; |
| 87 | }), |
| 88 | ); |
| 89 | ``` |
| 90 | |
| 91 | Task governance arbitrates the fleet between teams: |
| 92 | [`ClusterSchedulerConfig`](/providers/aws/sagemaker/clusterschedulerconfig) |
| 93 | sets priority classes and |
| 94 | [`ComputeQuota`](/providers/aws/sagemaker/computequota) reserves |
| 95 | per-team capacity (Kueue under the hood). A workload submits |
| 96 | through governance by passing the quota resource: |
| 97 | |
| 98 | ```typescript |
| 99 | hyperpod: { |
| 100 | instanceGroup: hyperpod.instanceGroups.workers, |
| 101 | quota, // → hyperpod-ns-research + queue label |
| 102 | priorityClass: "training", |
| 103 | }, |
| 104 | ``` |
| 105 | |
| 106 | Docs: [HyperPod](/aws/compute/hyperpod). The full working stack |
| 107 | (both orchestrators, governance, every workload tier) is |
| 108 | [`examples/aws-hyperpod`](https://github.com/alchemy-run/alchemy/tree/main/examples/aws-hyperpod). |
| 109 | |
| 110 | ## Worker bundling fixes |
| 111 | |
| 112 | Two bundling bugs could break a deployed Worker even though the |
| 113 | deploy succeeded. Both are fixed. |
| 114 | |
| 115 | Hono/Express-style route registrations were stripped from the |
| 116 | bundle ([#952](https://github.com/alchemy-run/alchemy/pull/952)). |
| 117 | `purePlugin` annotated top-level calls like `app.get("/", |
| 118 | handler)` as pure, and under minification that annotation is a |
| 119 | deletion instruction — so every request 404'd. Statement-position |
| 120 | calls are now only annotated in packages that explicitly declare |
| 121 | `sideEffects: false`, and never in entry modules. |
| 122 | |
| 123 | Cross-chunk evaluation order is now correct |
| 124 | ([#940](https://github.com/alchemy-run/alchemy/pull/940)). |
| 125 | Rolldown could split top-level initializers (e.g. Drizzle |
| 126 | `pgTable` schemas) into a chunk that ran before its dependencies, |
| 127 | failing Cloudflare startup validation with `Cannot access 'a' |
| 128 | before initialization`. `strictExecutionOrder` is now on by |
| 129 | default, preserving ESM semantics across chunk boundaries. |
| 130 | |
| 131 | The same PR opens up per-Worker control of chunking: `build` |
| 132 | forwards rolldown output options and `preserveEntrySignatures`: |
| 133 | |
| 134 | ```typescript |
| 135 | Cloudflare.Worker("Api", { |
| 136 | main, |
| 137 | build: { |
| 138 | output: { |
| 139 | codeSplitting: { |
| 140 | groups: [{ name: "auth", test: "packages/(schema|auth)/" }], |
| 141 | }, |
| 142 | }, |
| 143 | }, |
| 144 | }); |
| 145 | ``` |
| 146 | |
| 147 | ## Destroy and nuke converge in one pass |
| 148 | |
| 149 | Destroying a stack with a VPC-attached Lambda used to strand |
| 150 | subnets and security groups on `DependencyViolation`, forcing |
| 151 | repeated `destroy` runs |
| 152 | ([#937](https://github.com/alchemy-run/alchemy/pull/937)). The |
| 153 | cause: Lambda's Hyperplane ENIs linger in the VPC for many |
| 154 | minutes after the function is deleted, and AWS's own reaper can |
| 155 | take 20+ minutes to clear them. Subnet and security-group deletes |
| 156 | now identify detached Lambda ENIs and delete them directly, so |
| 157 | one `destroy` removes everything. |
| 158 | |
| 159 | `alchemy unsafe nuke` gets the same treatment |
| 160 | ([#950](https://github.com/alchemy-run/alchemy/pull/950)): |
| 161 | |
| 162 | - `--independent` retries each delete on its own capped backoff |
| 163 | instead of a shared barrier, so one hanging delete only ever |
| 164 | stalls itself. |
| 165 | - Providers declare which resource types their cloud-side |
| 166 | teardown consumes (a HyperPod cluster deletes its ENIs by |
| 167 | assuming its IAM role inside its VPC), and nuke deletes in |
| 168 | topologically-ordered waves so a role is never deleted out from |
| 169 | under an in-flight cluster teardown. |
| 170 | |
| 171 | ## Also in this release |
| 172 | |
| 173 | - **`_redirects` and `_headers` now reach Cloudflare** |
| 174 | ([#928](https://github.com/alchemy-run/alchemy/pull/928)) — |
| 175 | the files were read and hashed but never sent, so redirect and |
| 176 | header rules were silently dropped. They now apply in deploys, |
| 177 | no-op redeploys, and `alchemy dev`. |
| 178 | - **Workflows under `alchemy dev`**: pausing a workflow no longer |
| 179 | reports `errored` |
| 180 | ([#934](https://github.com/alchemy-run/alchemy/pull/934)), and |
| 181 | workflow classes export correctly in vite dev |
| 182 | ([#933](https://github.com/alchemy-run/alchemy/pull/933)). |
| 183 | - **`Docker.Container` supports `labels` and `stopTimeout`** |
| 184 | ([#931](https://github.com/alchemy-run/alchemy/pull/931)) — |
| 185 | enabling Traefik-managed routing in a remote Docker context. |
| 186 | Thanks [Gerbuuun](https://github.com/Gerbuuun)! |
| 187 | - **`alchemy/Test/Vitest` is restored** |
| 188 | ([#947](https://github.com/alchemy-run/alchemy/pull/947)) — |
| 189 | the internal move to alchemy-test accidentally removed the |
| 190 | public vitest harness that shipped through beta.63. |
| 191 | - **Branded primitives stay opaque in Outputs** |
| 192 | ([#941](https://github.com/alchemy-run/alchemy/pull/941)) — a |
| 193 | `string & Brand<"UserId">` Action output is now `Output<UserId>` |
| 194 | instead of exploding into an `ObjectExpr` over every string |
| 195 | method. |
| 196 | - **SQL fragment helpers compose on wrapped clients** |
| 197 | ([#951](https://github.com/alchemy-run/alchemy/pull/951)) — |
| 198 | `` sql`INSERT INTO users ${sql.insert(row)}` `` and nested |
| 199 | `sql.and`/`sql.or` now replay against the resolved client, with |
| 200 | new live test suites covering the full `SqlClient` surface on |
| 201 | D1 and Postgres. |
| 202 | - **The alchemy-test plain reporter** prints a `[done/total]` |
| 203 | progress counter per test and a failed-test list at the end of |
| 204 | the run. |
| 205 | |
| 206 | ## Where to go next |
| 207 | |
| 208 | - [HyperPod](/aws/compute/hyperpod) |
| 209 | - [EKS](/aws/compute/eks) |
| 210 | - [Choosing a runtime](/aws/compute/choosing-a-runtime) |
| 211 | - [SQL](/sql) |
| 212 | - [CHANGELOG](https://github.com/alchemy-run/alchemy/blob/main/CHANGELOG.md#v200-beta65) |
| 213 | - [Compare v2.0.0-beta.64 → v2.0.0-beta.65](https://github.com/alchemy-run/alchemy/compare/v2.0.0-beta.64...v2.0.0-beta.65) |