SST logo

sst

Full-stack infrastructure defined in code

$ npx docs2skills add sst-infrastructure
SKILL.md

SST

Full-stack infrastructure defined in code

What this skill does

SST is a framework that makes it easy to build modern full-stack applications on your own infrastructure. What makes SST different is that your entire app is defined in code — in a single sst.config.ts file. This includes databases, buckets, queues, Stripe webhooks, or any one of 150+ providers.

With SST, everything is automated. You define components (like Next.js frontends, Lambda functions, databases) in TypeScript, and SST handles the deployment, resource linking, and local development environment. It's built on Pulumi but provides higher-level components designed for developers rather than DevOps engineers.

Prerequisites

  • Node.js 18+ (for Node projects)
  • AWS CLI configured with appropriate permissions
  • TypeScript knowledge
  • Docker (for container deployments)

Quick start

# Install SST
npm install sst

# Or globally (non-Node projects)
curl -fsSL https://sst.dev/install | bash

# Start development environment
sst dev

# Deploy to production
sst deploy --stage production

Minimal sst.config.ts:

export default {
  config() {
    return {
      name: "my-app",
      region: "us-east-1",
    };
  },
  stacks(app) {
    app.stack(function Site({ stack }) {
      new sst.aws.Nextjs("MyWeb", {
        domain: "my-app.com"
      });
    });
  },
};

Core concepts

Components: High-level building blocks that define parts of your app (frontends, APIs, databases). Components create necessary AWS infrastructure automatically.

Stages: Environment namespaces (dev, production, pr-123) that allow multiple deployments of the same app.

Resource Linking: Connect components together without hardcoding values. Link a bucket to a function, then access it via SST's SDK.

Drop-in Mode: Add SST to existing projects with just sst.config.ts in the root.

Monorepo: Split infrastructure into infra/ directory with packages for functions, frontend, backend.

Key API surface

Components

// Frontend
new sst.aws.Nextjs("MyWeb", { domain: "my-app.com" });
new sst.aws.Remix("MyRemix", { domain: "remix.com" });

// Backend
new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler",
  timeout: "3 minutes",
  memory: "1024 MB"
});

new sst.aws.Service("MyService", {
  cluster,
  link: [bucket],
  loadBalancer: { ports: [{ listen: "80/http" }] }
});

// Database & Storage
new sst.aws.Postgres("MyDB", {});
new sst.aws.Bucket("MyBucket");

// Cron & Queue
new sst.aws.Cron("MyCron", {
  schedule: "rate(1 minute)",
  job: "src/cron.handler"
});

Resource Access

import { Resource } from "sst";

// Access linked resources
console.log(Resource.MyBucket.name);
console.log(Resource.MyDB.connectionString);

CLI Commands

sst dev                    # Start local development
sst deploy --stage prod    # Deploy to stage
sst remove --stage dev     # Remove stage

Common patterns

Full-stack app with API and database

const vpc = new sst.aws.Vpc("MyVpc");
const cluster = new sst.aws.Cluster("MyCluster", { vpc });
const bucket = new sst.aws.Bucket("MyBucket");

// API service
const api = new sst.aws.Service("MyAPI", {
  cluster,
  link: [bucket],
  loadBalancer: {
    ports: [{ listen: "80/http" }]
  }
});

// Frontend
new sst.aws.Nextjs("MyWeb", {
  domain: "my-app.com",
  link: [api],
  environment: {
    API_URL: api.url
  }
});

Lambda function with S3 trigger

const bucket = new sst.aws.Bucket("MyBucket");
const processor = new sst.aws.Function("Processor", {
  handler: "src/process.handler",
  link: [bucket]
});

bucket.subscribe("src/trigger.handler", {
  events: ["s3:ObjectCreated:*"]
});

Custom transforms for advanced configuration

new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler",
  transform: {
    role: (args) => ({
      name: `${args.name}-CustomRole`,
      assumeRolePolicy: customPolicy
    })
  }
});

Multi-provider setup

// AWS backend
const api = new sst.aws.Function("API", {
  handler: "src/api.handler"
});

// Vercel frontend
new vercel.Project("MyFrontend", {
  name: "my-nextjs-app"
});

// Stripe products
new stripe.Product("MyProduct", {
  name: "SST Paid Plan",
  description: "This is how SST makes money"
});

Configuration

Basic config structure

export default {
  config(input) {
    return {
      name: "my-app",
      region: "us-east-1",
      profile: input?.stage === "production" ? "prod" : "dev"
    };
  },
  stacks(app) {
    app.stack(MyStack);
  }
};

Environment-specific configuration

config(input) {
  return {
    name: "my-app",
    region: input?.stage === "production" ? "us-west-2" : "us-east-1"
  };
}

Best practices

  1. Use stages for environments: Deploy dev with sst deploy --stage dev, production with sst deploy --stage production
  2. Link resources instead of hardcoding: Use link: [bucket] and access via Resource.MyBucket.name
  3. Start with drop-in mode: Add sst.config.ts to existing projects before moving to monorepo
  4. Use transforms sparingly: Only when high-level component options aren't enough
  5. Leverage sst dev: Unified development environment handles frontend, backend, and infrastructure
  6. Keep components in logical stacks: Group related resources together
  7. Use meaningful component names: They become resource names in AWS
  8. Test with temporary stages: Use sst deploy --stage pr-123 for testing

Gotchas and common mistakes

Stage naming: Stage names become part of resource names. Use consistent, URL-safe names.

Component names are permanent: Changing a component name creates new resources and destroys old ones. Plan names carefully.

VPC requirements: Some components (like RDS) require VPC. Create sst.aws.Vpc first.

Domain setup: Custom domains need Route53 hosted zones configured outside SST initially.

Resource linking only works at runtime: Resource.MyBucket.name only works in deployed functions, not at build time.

Transforms vs high-level props: Always check if component has built-in prop before using transforms.

Memory limits: Lambda functions default to 1024MB. Increase for heavy workloads.

Cold starts: Consider using reservedConcurrency for latency-sensitive functions.

Local development: sst dev requires Docker for container services and proper AWS permissions.

Deployment order: SST handles dependencies automatically, but circular references will fail.

Provider versions: Some Pulumi providers may conflict. Pin versions if issues arise.

SST vs Pulumi accounts: SST doesn't require Pulumi Cloud account - everything stays local.

Resource removal: sst remove destroys everything. Use --stage carefully.

Container services need clusters: Always create sst.aws.Cluster before sst.aws.Service.

Function timeouts: Default is 15 seconds. Set explicitly for long-running tasks.