← All articles
CICD Serverless Frameworks Compared 2026-02-09 · 10 min read · serverless · aws · infrastructure

Serverless Frameworks Compared

CICD 2026-02-09 · 10 min read serverless aws infrastructure sst terraform pulumi deployment lambda

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.

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:

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

Cons

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

Cons

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

Cons

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:

// 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

Cons

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:

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

Cons

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?

Framework Code-only Change Infrastructure Change
SST (sst deploy) 5-15 seconds 30-90 seconds
SST (sst dev -- live) Instant (local) N/A
Serverless Framework 30-60 seconds 60-180 seconds
AWS SAM (sam sync) 5-10 seconds 60-180 seconds
AWS SAM (sam deploy) 60-120 seconds 60-180 seconds
Pulumi 10-30 seconds 30-120 seconds
Terraform 15-45 seconds 30-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.