Skip to content

Choosing a runtime

AWS gives you several places to run code. They differ in one main way: how much of the machine AWS manages for you, and so how much you decide yourself. Every one of them runs the same kind of Alchemy program, takes the same bindings, and gets its IAM role from them.

Lambdaper invocation
Web FunctionHTTP server
ECSon Fargate
EKSAuto Mode
EC2a machine
your program
you
you
you
you
you
scaling
AWS
AWS
you
you
you
image & runtime
AWS
AWS
you
you
you
orchestration
AWS
AWS
AWS
you
you
OS & patching
AWS
AWS
AWS
AWS
you
hardware
AWS
AWS
AWS
AWS
AWS
From left to right, AWS manages less and you decide more.

Whichever you pick, you declare it with an id, its configuration and an Effect. Construction binds what it uses, and the handlers it returns do the work. Here it is as an ECS Service:

alchemy.run.ts
const api = yield* AWS.ECS.Service(
"Api",
{ cluster, main: import.meta.url, port: 3000, loadBalancer: true },
Effect.gen(function* () {
const getObject = yield* AWS.S3.GetObject(Files);
return {
fetch: Effect.succeed(HttpServerResponse.text("Hello from ECS!")),
};
}).pipe(Effect.provide(AWS.S3.GetObjectHttp)),
);

The same Effect, with its bindings and its fetch, runs as a Lambda Function or on EC2.

So moving part of an app from one runtime to another changes its declaration, not its code. Choose by how the work behaves, and change your mind later if it behaves differently than you expected.

A Lambda Function runs when something invokes it: a request, a queue message, a new object, a schedule. AWS starts environments as invocations arrive and stops them when they don’t, so an idle Function costs nothing and a busy one scales with no setup.

That makes it an easy and cheap place to start. Its limits come from the same model: an invocation runs for at most 15 minutes, each environment handles one invocation at a time, and a new environment takes a cold start before its first one.

Choose it for event handlers, background jobs, scheduled work, and APIs that see uneven traffic.

environment 1invocationinvocationenvironment 2invocation
invocationcold start
One invocation per environment. An invocation that arrives while the others are busy starts a new environment, with a cold start.

Lambda Web Functions: HTTP servers on Lambda

Section titled “Lambda Web Functions: HTTP servers on Lambda”

A Lambda Web Function runs an HTTP server on Lambda. One environment serves many requests at once, up to 128, so a spike starts fewer environments and requests share warm connections. Responses stream as your handler produces them.

It only receives HTTP, runs Node.js, and has a fixed 2 GB of memory per environment. Choose it for APIs and websites, and pair it with a Lambda Function when the API hands work to a queue.

environment 1up to 128 at oncerequestrequestrequestrequest
The same traffic in one environment. Requests run side by side and share its warm connections.

ECS runs your program as a container on Fargate, and keeps it running. There’s no time limit and no cold start per request, and the program can hold connections, WebSockets or in-memory state for as long as it’s up.

You pay for running tasks whether or not they’re busy. They run inside a VPC: your account’s default one to start, or one you build with the Network helper. Alchemy builds the image from your program, or from a Dockerfile, and pushes it for you.

Choose it for long-lived servers, workers that hold connections open, and anything that already ships as a container.

requeststask 1running · holds connections and memorytask 2running
Tasks run whether or not requests arrive, so there's no cold start, and you pay for every hour they run.

EKS runs a Kubernetes cluster, with AWS managing the control plane and, in Auto Mode, the nodes. You deploy Deployments, Jobs, Helm charts and raw manifests from the same Stack, with the same bindings.

A cluster takes about ten minutes to create and costs money while it exists. Choose it when you want Kubernetes itself: its tools, its operators, or a team that already runs on it.

EC2 gives you a virtual machine. You pick the instance type, the operating system and the disk, and you own patching and recovery. Alchemy can run your program on it directly, or leave it as a plain machine.

Choose it for GPUs, custom daemons, software that expects a host, or steady load where a reserved machine is cheapest.

An app rarely needs just one. A common shape is a Web Function for the API, a Lambda Function consuming the queue it writes to, and an ECS service for the one process that must stay connected:

ApiLambda Web FunctionJobsSQS QueueWorkerLambda FunctionFeedECS Service

Each runtime has its own role, written by its own bindings, so each part of the app can reach only what it uses.