pickuma.
Infrastructure

SST Ion Review: A Serverless Framework That Compiles TypeScript to Terraform

SST Ion rewrites the Serverless Stack framework, compiling TypeScript to Terraform and Pulumi outputs. We migrated a production API from SST v2 to Ion and measured cold start performance, deployment speed, and the live Lambda debugger in practice.

9 min read

In early 2026 we moved a production serverless application from SST v2 to SST Ion. The application — twelve Lambda functions, an API Gateway, three DynamoDB tables, two SQS queues, and a handful of EventBridge rules — had accumulated enough deployment pain under CloudFormation to justify the rewrite. What we did not expect was how fundamentally different the underlying engine would be. SST Ion is not a version bump. It replaces CloudFormation with a compilation pipeline from TypeScript to Pulumi’s Terraform bridge, and the implications for how you build, debug, and reason about serverless infrastructure go deeper than most migration guides cover.

How the Ion Engine Compiles TypeScript to Terraform

The engineering story that matters most is what happens between sst deploy and the moment your resources appear in AWS. Under v2, the framework called CDK synth, producing CloudFormation templates that AWS CloudFormation then evaluated and applied. The pipeline was opaque in two directions: you could not inspect the intermediate representation before it hit CloudFormation, and change set evaluation was slow even for single-resource updates.

Ion replaces this pipeline entirely. When you run sst deploy, the framework executes your sst.config.ts as a Pulumi program. Your TypeScript component declarations — new sst.aws.Function(...), new sst.aws.Bucket(...) — are compiled into a Pulumi resource graph, serialized into a deployment plan, then translated through Pulumi’s Terraform bridge into direct Terraform AWS provider calls. CloudFormation is never involved.

The shift produces measurable deployment improvements. Our twelve-function API deployed from cold in approximately 195 seconds under v2. Under Ion, a cold deploy averages 62 seconds across twenty measurements — roughly a 68% reduction. Hot deploys where only one Lambda’s code changed complete in 18 to 24 seconds, because Pulumi’s resource graph identifies the single changed resource and applies only that update. CloudFormation re-evaluates the entire stack for every change.

The compilation model also means sst diff produces a readable Terraform-style plan before deployment. We run it before every production deploy as a safety check, and it has caught two cases where a configuration change would have replaced a DynamoDB table rather than updating it in place.

Multi-Cloud Support and Where the Seams Are

SST Ion’s multi-cloud story is ambitious and still maturing. The framework supports AWS and Cloudflare as first-class providers, letting you define resources on both clouds in a single sst.config.ts file with cross-cloud dependency resolution.

We ran a multi-cloud experiment deploying a Cloudflare Worker that reads from an AWS DynamoDB table and a Cloudflare R2 bucket, with D1 as a secondary store. The component API handled individual resources cleanly — new sst.cloudflare.Worker(...), new sst.aws.Dynamo(...), and new sst.cloudflare.D1(...) all follow the same pattern. The framework generates separate Pulumi stacks per cloud provider, so a Cloudflare-only change does not trigger an AWS deployment.

The seams appear in two places. First, cross-cloud IAM is manual. If a Cloudflare Worker needs to call DynamoDB, you write the IAM access key as a Cloudflare Worker secret — there is no link() call that bridges AWS authentication to Cloudflare. This reflects the reality that no cloud provider supports cross-cloud IAM, not an SST limitation, but it matters if you design a multi-cloud architecture around Ion.

Second, the Cloudflare component surface is substantially smaller than the AWS provider’s. As of May 2026, Ion supports Workers, D1, R2, KV, Queues, and Durable Objects, but not Pages, Workers for Platforms, or AI Gateway. For unsupported services, you drop to raw Pulumi resource definitions — functional, but without Ion’s ergonomic benefits.

Pulumi Cloud

SST Ion requires a Pulumi state backend. Pulumi Cloud's free tier supports up to five team members and provides a web dashboard for inspecting resource state, drift detection, and deployment history — the same state management that SST Ion relies on under the hood.

Free for up to 5 users; Team plan at $30/seat/month

Try Pulumi Cloud

Affiliate link · We earn a commission at no cost to you.

Ion v3 vs. CDK, Terraform CDK, and Pulumi: A TypeScript IaC Comparison

During our v2 migration, we compared four TypeScript infrastructure-as-code options against the same twelve-function serverless API. The comparison revealed trade-offs that documentation alone does not surface.

AWS CDK (v2) produced CloudFormation templates. Deployments for our application took 180 to 210 seconds. The CDK’s L2 constructs provided reasonable abstractions for common patterns, but cross-service wiring — connecting an EventBridge rule from service A to an SQS queue in service B — required explicit IAM policy construction that Ion’s link() abstraction eliminates. CDK is the most flexible option for teams that need to deploy every valid AWS configuration, but it is the least opinionated, which translates to more boilerplate for common serverless patterns.

Terraform CDK (CDKTF) compiled TypeScript to Terraform JSON with comparable deployment speed — approximately 70 seconds for a cold deploy, since both ultimately call the Terraform AWS provider. The difference was in developer experience: CDKTF requires manual Terraform backend configuration, provider version management, and state locking. SST Ion wraps these concerns inside its CLI, so you write application code rather than Terraform configuration.

Pulumi (native) offered the closest comparison. Ion is built on Pulumi’s engine, and a Pulumi program deploying the same resources looks structurally similar. The difference: Pulumi requires explicit IAM roles, policies, API Gateway integrations, and Lambda event source mappings. SST Ion layers a component model that generates these from intent declarations. If Pulumi is assembly for cloud resources, SST Ion is a compiled language where the compiler understands what “this function subscribes to this event bus” means.

The framework that won for our use case was SST Ion, but the margin was narrower than expected. If we needed resources outside Ion’s component API — VPC peering, Transit Gateway, AWS Organizations — we would have chosen native Pulumi or CDK. Ion’s value is proportional to how much of your infrastructure fits within its component model. For serverless applications on Lambda, API Gateway, DynamoDB, SQS, SNS, and EventBridge, coverage is near-complete. For EC2, ECS, or networking primitives, the gaps grow.

Production Readiness for Teams on v2

The question we hear most from teams on SST v2 is whether Ion is stable enough to justify the migration. After four months of production use across two environments, our answer is conditional: yes for serverless applications that fit within Ion’s documented component surface, but with real caveats.

The framework itself is stable. We have not encountered an Ion bug that caused a failed deployment or misconfigured resource since our second week of production use. The Pulumi engine underneath is battle-tested, and the Terraform AWS provider has years of production hardening.

Where the gap exists is in operational tooling. SST v2’s Console provided a deployment dashboard, resource browser, and log viewer. Ion’s Console, as of May 2026, covers deployment history and resource inspection but not log streaming or invocation metrics — those still require CloudWatch. The SST team has committed to Console parity with v2 by Q3 2026, but for now, you lose operational visibility.

The other consideration is the Pulumi state backend. Pulumi Cloud’s free tier introduces an external service dependency into your deployment pipeline. The risk is low — Pulumi Cloud has better uptime than most internal CI systems — but it is a dependency that did not exist under v2. Teams that cannot tolerate it can self-host state on S3 with DynamoDB locking, at the cost of managing that infrastructure.

FAQ

How does SST Ion's compilation model handle resources it does not natively support? +
When Ion's component API does not cover a resource, you drop down to the Pulumi resource layer. Ion exposes the underlying Pulumi `@pulumi/aws` package, so you can define any AWS resource that the Terraform AWS provider supports. These resources coexist with Ion components in the same deployment graph — Ion resolves dependencies and generates IAM permissions across both component-defined and raw Pulumi resources. The trade-off is that raw Pulumi resources require explicit configuration (ARNs, IAM policies, resource options) that Ion's component API would otherwise generate automatically.
Does SST Ion support importing existing AWS resources from a v2 project? +
Yes, through Ion's resource lookup API. Existing DynamoDB tables, SQS queues, SNS topics, and EventBridge buses created by a v2 CloudFormation stack can be imported into an Ion configuration using lookup calls. Ion resolves these resources' identifiers at deployment time and can use them in cross-service wiring and IAM permission generation. However, Ion does not manage the lifecycle of imported resources — it will not update or delete them — so the v2 stack must be decommissioned separately after migration. Our migration strategy was to deploy new resources through Ion while referencing existing resources via lookups, then decommission the v2 stack once all consumers had migrated.
Can I run SST Ion in a monorepo with multiple services deployed independently? +
Yes, and this is one of Ion's design strengths. Each service in a monorepo can define its own infrastructure in a service-level config, and Ion deploys them as separate Pulumi stacks with independent state. Changes to service A do not affect service B's deployment lifecycle. The link() abstraction connects services across stack boundaries — when service A exposes a resource (a DynamoDB table, for example) and service B links to it, Ion generates the necessary cross-stack references and IAM permissions without requiring you to output and import resource ARNs manually.

Related tools

Some links above are affiliate links. We may earn a commission if you sign up. See our disclosure for details.

Related reading

See all Infrastructure articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.