Skip to main content

Command Palette

Search for a command to run...

Serverless Frameworks Compared

Published
•12 min read•View as Markdown
D
Practical guides for developers: TypeScript, developer tools, CI/CD, and modern web development. We cover the tools that make devs more productive.

Serverless Frameworks Compared

Serverless computing promised to free developers from infrastructure management. The irony is that choosing how to deploy serverless functions has become its own infrastructure decision, with a half-dozen frameworks competing to be your deployment layer.

AWS Lambda serverless computing logo

This guide compares the frameworks that matter in 2026: SST (Ion), Serverless Framework v4, AWS SAM, Pulumi, and Terraform. Each takes a fundamentally different approach to the same problem -- getting your code running on Lambda (or equivalent) with the right triggers, permissions, and infrastructure around it.

The Quick Version

If you want the recommendation up front:

  • Building a full-stack app with AWS? SST Ion.
  • Just deploying Lambda functions, nothing fancy? AWS SAM.
  • Multi-cloud or complex infrastructure beyond serverless? Pulumi or Terraform.
  • Migrating from Serverless Framework v3? Evaluate whether to move to SST or SAM rather than upgrading to v4.

Now let's dig into why.

SST (Ion)

SST -- originally the Serverless Stack Toolkit -- reinvented itself in 2024 with Ion, a complete rewrite that replaced its CloudFormation-based architecture with Pulumi/Terraform under the hood. It's the most opinionated framework on this list, and for full-stack applications on AWS, it's the best developer experience available.

How It Works

SST Ion uses a TypeScript-first configuration model. You define your infrastructure in sst.config.ts:

// sst.config.ts
export default $config({
  app(input) {
    return {
      name: "my-app",
      removal: input.stage === "production" ? "retain" : "remove",
      home: "aws",
    };
  },
  async run() {
    const bucket = new sst.aws.Bucket("Uploads");
    const api = new sst.aws.Function("Api", {
      handler: "packages/functions/src/api.handler",
      link: [bucket],
      url: true,
    });

    const site = new sst.aws.Nextjs("Site", {
      link: [api, bucket],
    });

    return {
      api: api.url,
      site: site.url,
    };
  },
});

The Killer Feature: Resource Linking

SST's link system is genuinely innovative. When you link a resource to a function, SST automatically:

  1. Grants the IAM permissions needed
  2. Injects the resource's connection details as environment variables
  3. Provides a type-safe SDK to access linked resources
// In your function code
import { Resource } from "sst";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({});
await s3.send(new PutObjectCommand({
  Bucket: Resource.Uploads.name,  // Type-safe reference
  Key: "file.txt",
  Body: "hello",
}));

No more manually wiring IAM policies. No more hardcoded bucket names or ARNs. The framework handles the plumbing.

Local Development: sst dev

sst dev starts a local development environment that proxies Lambda invocations from AWS to your local machine. Your functions run locally with hot reload, but they interact with real AWS resources (DynamoDB, S3, SQS, etc.).

sst dev

This is dramatically better than the emulator-based approach (SAM local, LocalStack) because you're testing against real AWS services. The trade-off is that you need AWS credentials and an internet connection for local dev.

Pros

  • Best-in-class DX for full-stack AWS applications
  • Type-safe infrastructure configuration
  • Resource linking eliminates IAM policy boilerplate
  • Excellent local development with live Lambda debugging
  • Built-in support for Next.js, Remix, Astro, and other frameworks
  • Multiplayer stages -- each developer gets their own isolated stack

Cons

  • AWS-only (no multi-cloud)
  • Opinionated -- if you disagree with SST's opinions, you'll fight the framework
  • Ion is relatively new (2024) -- some rough edges remain
  • Smaller community than Terraform or Serverless Framework
  • Uses Pulumi under the hood, which adds a dependency layer

Cost

SST the framework is free and open source. SST Console (their dashboard) has a free tier and paid plans starting at $20/month per workspace.

Serverless Framework v4

The Serverless Framework was the original tool that popularized serverless development starting in 2015. It uses YAML configuration and a plugin ecosystem to deploy Lambda functions with triggers.

The v4 Licensing Controversy

Serverless Framework v4 (released 2024) introduced a significant licensing change: it requires a paid license for organizations with over $2M in annual revenue. This drove a substantial portion of the community to migrate to other tools.

If you're under the revenue threshold, it's still free. But the licensing change signals a direction that makes many teams uncomfortable.

Configuration

# serverless.yml
service: my-service
frameworkVersion: "4"

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1
  environment:
    UPLOADS_BUCKET: !Ref UploadsBucket

functions:
  api:
    handler: src/api.handler
    events:
      - httpApi:
          method: GET
          path: /items

  processUpload:
    handler: src/process.handler
    events:
      - s3:
          bucket: !Ref UploadsBucket
          event: s3:ObjectCreated:*

resources:
  Resources:
    UploadsBucket:
      Type: AWS::S3::Bucket

Plugin Ecosystem

The Serverless Framework's plugin ecosystem is its biggest advantage. Need offline development? serverless-offline. Need webpack bundling? serverless-webpack. Need per-function IAM roles? serverless-iam-roles-per-function. There's a plugin for almost everything.

plugins:
  - serverless-offline
  - serverless-esbuild
  - serverless-iam-roles-per-function

Pros

  • Mature ecosystem with hundreds of plugins
  • Large community -- lots of Stack Overflow answers and blog posts
  • Multi-cloud support (AWS, Azure, GCP) though AWS is the primary focus
  • Simple YAML configuration for straightforward Lambda deployments
  • Dashboard with monitoring, CI/CD, and observability

Cons

  • v4 licensing is a dealbreaker for many organizations
  • YAML configuration gets painful at scale -- deeply nested, error-prone, hard to refactor
  • CloudFormation under the hood means slow deployments and cryptic error messages
  • Local development (serverless-offline) is a mediocre emulator
  • No type safety in configuration

When to Use It

Honestly? If you're starting a new project in 2026, there are better options. If you have an existing Serverless Framework v3 project, evaluate migration costs to SST or SAM rather than upgrading to v4. The licensing change and the YAML-first approach make it less compelling than the alternatives.

AWS SAM (Serverless Application Model)

SAM is AWS's official serverless framework. It extends CloudFormation with serverless-specific resource types and provides a CLI for local development, testing, and deployment.

Configuration

# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Runtime: nodejs20.x
    Timeout: 30
    MemorySize: 256

Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/api.handler
      Events:
        GetItems:
          Type: HttpApi
          Properties:
            Method: GET
            Path: /items
      Policies:
        - S3ReadPolicy:
            BucketName: !Ref UploadsBucket

  UploadsBucket:
    Type: AWS::S3::Bucket

Local Development

SAM provides Docker-based local emulation:

# Invoke a function locally
sam local invoke ApiFunction --event events/get-items.json

# Start a local API Gateway
sam local start-api

# Start a local Lambda endpoint
sam local start-lambda

The local emulation is decent for basic testing but doesn't replicate all AWS service behaviors. DynamoDB queries, S3 operations, and SQS interactions need a real AWS environment or LocalStack for accurate testing.

SAM Accelerate

sam sync (SAM Accelerate) speeds up the development cycle by bypassing CloudFormation for code-only changes:

sam sync --watch --stack-name my-app-dev

This watches for file changes and deploys code updates in seconds instead of minutes. Infrastructure changes still go through CloudFormation, but code changes are pushed directly to Lambda.

Pros

  • First-party AWS tool -- guaranteed compatibility and long-term support
  • Free, open source, no licensing concerns
  • Direct CloudFormation integration -- anything CloudFormation supports, SAM supports
  • SAM Accelerate makes iterative development fast
  • Policy templates simplify IAM (e.g., S3ReadPolicy, DynamoDBCrudPolicy)
  • Strong integration with AWS tooling (CodePipeline, CodeBuild, X-Ray)

Cons

  • YAML/CloudFormation syntax is verbose and unforgiving
  • Local emulation is limited compared to SST's live dev approach
  • AWS-only (by design)
  • No plugin ecosystem -- you get what SAM provides
  • CloudFormation deployment speed is slow for infrastructure changes (minutes, not seconds)
  • Error messages from CloudFormation are often unhelpful

When to Use It

SAM is the right choice when: you want the safety of an official AWS tool, your serverless project is primarily Lambda + API Gateway + DynamoDB, and you don't need the DX features of SST. It's also the right choice for organizations with strict vendor requirements that mandate first-party tools.

Pulumi

Pulumi is an infrastructure-as-code tool that lets you define infrastructure using general-purpose programming languages (TypeScript, Python, Go, C#, Java). It's not serverless-specific, but it's excellent at serverless deployments.

Configuration

// index.ts
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";

const bucket = new aws.s3.Bucket("uploads");

const api = new awsx.apigateway.API("api", {
  routes: [
    {
      path: "/items",
      method: "GET",
      eventHandler: new aws.lambda.Function("getItems", {
        runtime: "nodejs20.x",
        handler: "index.handler",
        code: new pulumi.asset.AssetArchive({
          ".": new pulumi.asset.FileArchive("./src/get-items"),
        }),
        environment: {
          variables: {
            BUCKET_NAME: bucket.bucket,
          },
        },
      }),
    },
  ],
});

const role = new aws.iam.RolePolicyAttachment("lambdaS3", {
  role: api.routes[0].eventHandler.role.name,
  policyArn: aws.iam.ManagedPolicy.AmazonS3ReadOnlyAccess,
});

export const apiUrl = api.url;
export const bucketName = bucket.bucket;

Why Pulumi for Serverless

The advantage of Pulumi over YAML-based tools becomes clear at scale:

  • Loops and conditionals. Deploy 10 Lambda functions with a for loop, not 10 copy-pasted YAML blocks.
  • Abstraction. Write reusable components as functions or classes.
  • Type safety. TypeScript catches misconfigurations at compile time.
  • Testing. Unit test your infrastructure with standard testing frameworks.
// Reusable component
function createApiHandler(name: string, path: string, handlerDir: string) {
  return new aws.lambda.Function(name, {
    runtime: "nodejs20.x",
    handler: "index.handler",
    code: new pulumi.asset.AssetArchive({
      ".": new pulumi.asset.FileArchive(handlerDir),
    }),
  });
}

Pros

  • Real programming languages -- loops, conditionals, abstractions, testing
  • Multi-cloud support (AWS, Azure, GCP, Kubernetes)
  • Type-safe infrastructure configuration
  • Can import existing CloudFormation/Terraform resources
  • State management (Pulumi Cloud or self-managed backends)
  • Component model for reusable infrastructure patterns

Cons

  • Not serverless-specific -- you're writing lower-level infrastructure code
  • More verbose than SST for common serverless patterns
  • Learning curve for the Pulumi resource model
  • Pulumi Cloud (state management) is a paid service for teams (self-hosted backend is free)
  • No built-in local development for serverless functions

When to Use It

Pulumi is the right choice when: you need multi-cloud, your infrastructure extends well beyond serverless, you want to use real programming languages for IaC, or you're already using it for other infrastructure.

Terraform

Terraform uses HCL (HashiCorp Configuration Language) and is the most widely adopted infrastructure-as-code tool. Like Pulumi, it's not serverless-specific, but plenty of teams use it for serverless deployments.

Configuration

# main.tf
resource "aws_lambda_function" "api" {
  function_name = "api-handler"
  runtime       = "nodejs20.x"
  handler       = "index.handler"
  filename      = data.archive_file.api.output_path
  role          = aws_iam_role.lambda.arn

  environment {
    variables = {
      BUCKET_NAME = aws_s3_bucket.uploads.bucket
    }
  }
}

resource "aws_s3_bucket" "uploads" {
  bucket_prefix = "uploads-"
}

resource "aws_apigatewayv2_api" "api" {
  name          = "api"
  protocol_type = "HTTP"
}

resource "aws_apigatewayv2_integration" "api" {
  api_id             = aws_apigatewayv2_api.api.id
  integration_type   = "AWS_PROXY"
  integration_uri    = aws_lambda_function.api.invoke_arn
  payload_format_version = "2.0"
}

resource "aws_apigatewayv2_route" "get_items" {
  api_id    = aws_apigatewayv2_api.api.id
  route_key = "GET /items"
  target    = "integrations/${aws_apigatewayv2_integration.api.id}"
}

# Plus IAM role, policy attachments, API stage, Lambda permission...

The Verbosity Problem

Look at that Terraform config. It's explicit and declarative, which is good for infrastructure teams managing hundreds of resources. But for a simple "Lambda function behind API Gateway," it's a lot of boilerplate. You need to manually wire up:

  • IAM role and policy attachments
  • API Gateway integration
  • API Gateway route
  • Lambda permission for API Gateway to invoke the function
  • API Gateway stage and deployment

SST and SAM handle all of this with a few lines. Terraform makes you spell it out.

Terraform Modules

The community addresses this with modules:

module "lambda_function" {
  source  = "terraform-aws-modules/lambda/aws"
  version = "~> 7.0"

  function_name = "api-handler"
  handler       = "index.handler"
  runtime       = "nodejs20.x"
  source_path   = "./src/api"

  attach_policy_json = true
  policy_json = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["s3:GetObject"]
      Resource = "${aws_s3_bucket.uploads.arn}/*"
    }]
  })
}

OpenTofu

Worth mentioning: OpenTofu is the open-source fork of Terraform, created after HashiCorp changed Terraform's license to BSL in 2023. It's API-compatible with Terraform and backed by the Linux Foundation. If licensing matters to you, OpenTofu is the drop-in replacement.

Pros

  • Largest IaC ecosystem -- providers for every cloud and service
  • HCL is purpose-built for infrastructure (not a general-purpose language)
  • Massive community, documentation, and module library
  • terraform plan gives you a clear preview of changes before applying
  • State management is well-understood and battle-tested
  • Multi-cloud support

Cons

  • Very verbose for serverless patterns
  • HCL is limited -- no real loops (for_each is awkward), no functions, no abstractions
  • No local development support for serverless
  • No concept of "environments" or "stages" without wrapper tooling (Terragrunt)
  • State locking and management adds operational overhead

When to Use It

Terraform is the right choice when: your organization already uses it for other infrastructure, you need multi-cloud, your platform team manages infrastructure centrally, or you want the largest ecosystem of providers and modules.

Deployment Speed Comparison

How long does it take to deploy a change to a single Lambda function?

FrameworkCode-only ChangeInfrastructure Change
SST (sst deploy)5-15 seconds30-90 seconds
SST (sst dev -- live)Instant (local)N/A
Serverless Framework30-60 seconds60-180 seconds
AWS SAM (sam sync)5-10 seconds60-180 seconds
AWS SAM (sam deploy)60-120 seconds60-180 seconds
Pulumi10-30 seconds30-120 seconds
Terraform15-45 seconds30-120 seconds

CloudFormation-based tools (Serverless Framework, SAM without sync) are the slowest because every deployment creates a CloudFormation changeset.

Vendor Lock-in: An Honest Assessment

Every serverless framework involves some degree of lock-in. Here's where the lock-in actually lives:

AWS Lambda itself is the biggest lock-in. Your function code uses AWS SDK calls, IAM permissions, API Gateway request/response formats, and AWS-specific event sources. The framework you choose to deploy it is a minor concern compared to the runtime lock-in.

SST locks you into AWS and SST's abstraction layer. If SST disappears, you'd need to recreate infrastructure in another tool.

Serverless Framework is the most portable in theory (multi-cloud), but in practice, most serverless.yml files are deeply AWS-specific.

SAM locks you into AWS and CloudFormation. Since it's a CloudFormation extension, your templates are portable to any CloudFormation tool.

Pulumi and Terraform are the most portable frameworks -- they support multiple clouds, and your infrastructure code is decoupled from any specific serverless model.

The honest truth: if you're building on AWS Lambda, you're locked into AWS regardless of framework. Optimize for developer experience, not theoretical portability.

The Bottom Line

For full-stack applications on AWS: SST Ion. The developer experience -- live Lambda debugging, resource linking, type-safe config -- is a generation ahead of everything else.

For simple Lambda deployments on AWS: AWS SAM. It's free, first-party, and SAM Accelerate makes the development cycle fast. The YAML is annoying but manageable for small projects.

For multi-cloud or infrastructure-heavy projects: Pulumi if you want to use TypeScript/Python, Terraform if your team already knows HCL.

For new projects: Don't start with Serverless Framework v4. The licensing situation and the availability of better alternatives make it hard to justify.

The serverless framework you choose matters less than your application architecture. Pick one that fits your team's existing skills, deploy with it consistently, and focus your energy on the code running inside the functions. That's where the actual value is.


Enjoyed this guide? Subscribe to DevTools Guide — a free weekly newsletter covering developer tools, workflows, and best practices.

More from this blog

DevTools Guide

183 posts