sst Documentation

Build full-stack apps on your own infrastructure.

26,369 stars TypeScript TypeScript 97 files Β· ~160,965 tokens
RAW Doc

Technical Documentation: anomalyco/sst

ℹ️ Provenance: Multi-Tier Ingestion Β· CodeWiki Reference

Overview (README.md)


Build full-stack apps on your own infrastructure.

Installation

For JavaScript projects, install SST locally so the CLI version is tracked with your app. You can then run the CLI with the same package manager.

bash
npm install sst
# pnpm add sst
# bun add sst
# yarn add sst

If you are not using JavaScript, you can install the CLI globally.

bash
curl -fsSL https://sst.dev/install | bash

To install a specific version.

bash
curl -fsSL https://sst.dev/install | VERSION=0.0.403 bash

To use a package manager, check out our docs.

Manually

Download the pre-compiled binaries from the releases page and copy to the desired location.

Get Started

Get started with your favorite framework:

Learn More

Learn more about some of the key concepts:

Contributing

Here's how you can contribute:

  • Help us improve our docs
  • Find a bug? Open an issue
  • Feature request? Submit a PR

Running Locally

Run bun run setup. You need Go and Bun installed.

Now you can run the CLI locally on any of the examples/ apps.

bash
cd examples/aws-api
go run ../../cmd/sst <command>

If you want to build the CLI binary, run bun run build:cli. This will create a sst binary that you can use.

For building the docs, run bun run docs:generate and bun run docs:dev.


Join our community Discord | YouTube | X.com


Www Src Content Docs Blog Index (`www/src/content/docs/blog/index.mdx`)


title: Blog template: splash pagefind: false

import PostList from "../../../components/PostList.astro";


Www Src Content Docs Docs Index (`www/src/content/docs/docs/index.mdx`)


title: What is SST

import { Tabs, TabItem } from '@astrojs/starlight/components';
import { LinkCard } from '@astrojs/starlight/components';
import { Icon } from '@astrojs/starlight/components';
import VideoAside from "../../../components/VideoAside.astro";
import config from '../../../../config.ts';

export const github = config.github;

SST is a framework that makes it easy to build modern full-stack applications on your own infrastructure.

:::note
SST supports over 150 providers. Check out the full list.
:::

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.


Components

You start by defining parts of your app, in code.

For example, you can add your frontend and set the domain you want to use.

```ts title="sst.config.ts" new sst.aws.Nextjs("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new sst.aws.Remix("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new sst.aws.Astro("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new sst.aws.SvelteKit("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new sst.aws.SolidStart("MyWeb", { domain: "my-app.com" }); ```

Just like the frontend, you can configure backend features in code.

Like your API deployed in a container. Or any Lambda functions, Postgres databases, S3 Buckets, or cron jobs.

```ts title="sst.config.ts" const cluster = new sst.aws.Cluster("MyCluster", { vpc });

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

text
</TabItem>
<TabItem label="Functions">
```ts title="sst.config.ts"
new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler"
});
```ts title="sst.config.ts" new sst.aws.Postgres("MyDatabase", { vpc }); ``` ```ts title="sst.config.ts" new sst.aws.Bucket("MyBucket"); ``` ```ts title="sst.config.ts" new sst.aws.Cron("MyCronJob", { job: "src/cron.handler", schedule: "rate(1 minute)" }); ```

You can even set up your Stripe products in code.

ts title="sst.config.ts"
new stripe.Product("MyStripeProduct", {
  name: "SST Paid Plan",
  description: "This is how SST makes money",
});

You can check out the full list of components in the sidebar.


Infrastructure

The above are called Components. They are a way of defining the features of your application in code. You can define any feature of your application with them.

In the above examples, they create the necessary infrastructure in your AWS account. All without using the AWS Console.

Learn more about Components.


Configure

SST's components come with sensible defaults designed to get you started. But they can also be configured completely.

For example, the sst.aws.Function can be configured with all the common Lambda function options.

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

But with SST you can take it a step further and transform how the Function component creates its low level resources. For example, the Function component also creates an IAM Role. You can transform the IAM Role using the transform prop.

ts {3-7} title="sst.config.ts"
new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler",
  transform: {
    role: (args) => ({
      name: `${args.name}-MyRole`
    })
  }
});

Learn more about transforms.


Providers

SST has built-in components for AWS and Cloudflare that make these services easier to use.

However it also supports components from any one of the 150+ Pulumi/Terraform providers. For example, you can use Vercel for your frontends.

ts title="sst.config.ts"
new vercel.Project("MyFrontend", {
  name: "my-nextjs-app"
});

Learn more about Providers and check out the full list in the Directory.


Once you've added a couple of features, SST can help you link them together. This is great because you won't need to hardcode anything in your app.

Let's say you are deploying an Express app in a container and you want to upload files to an S3 bucket. You can link the bucket to your container.

ts title="sst.config.ts" {6}
const bucket = new sst.aws.Bucket("MyBucket");

const cluster = new sst.aws.Cluster("MyCluster", { vpc });

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

You can then use SST's SDK to access the S3 bucket in your Express app.

ts title="index.mjs" "resource.mybucket.name"
import { Resource } from "sst";

console.log(Resource.MyBucket.name);

Learn more about resource linking.


Project structure

We've looked at a couple of different types of files. Let's take a step back and see what an SST app looks like in practice.


Drop-in mode

The simplest way to run SST is to use it as a part of your app. This is called drop-in mode. For example, if you are building a Next.js app, you can add a sst.config.ts file to the root.

txt {3}
my-nextjs-app
β”œβ”€ next.config.js
β”œβ”€ sst.config.ts
β”œβ”€ package.json
β”œβ”€ app
β”œβ”€ lib
└─ public

View an <a href={${github}/tree/dev/examples/aws-nextjs}>example Next.js app using SST in drop-in mode.


Monorepo

Alternatively, you might use SST in a monorepo. This is useful because most projects have a frontend, a backend, and some functions.

In this case, the sst.config.ts is still in the root but you can split it up into parts in the infra/ directory.

txt {2,9}
my-sst-app
β”œβ”€ sst.config.ts
β”œβ”€ package.json
β”œβ”€ packages
β”‚Β Β β”œβ”€ functions
β”‚Β Β β”œβ”€ frontend
β”‚Β Β β”œβ”€ backend
│  └─ core
└─ infra

Learn more about our monorepo setup.


CLI

To make this all work, SST comes with a CLI. For JavaScript projects, install SST locally in your project so the CLI version is tracked with your app.

```bash npm install sst ``` ```bash pnpm add sst ``` ```bash bun add sst ``` ```bash yarn add sst ```

You can then run the CLI with your package manager.

```bash npx sst dev ``` ```bash pnpm exec sst dev ``` ```bash bun sst dev ``` ```bash yarn sst dev ```

If you are not using JavaScript, you can install the CLI globally.

bash
curl -fsSL https://sst.dev/install | bash

Learn more about the CLI.


Dev

The CLI includes a dev command that starts a local development environment.

bash
sst dev

This brings up a multiplexer that:

  1. Starts a watcher that deploys any infrastructure changes.
  2. Runs your functions Live, letting you make and test changes without having to redeploy them.
  3. Creates a tunnel to connect your local machine to any resources in a VPC.
  4. Starts your frontend and container services in dev mode and links it to your infrastructure.

The sst dev CLI makes it so that you won’t have to start your frontend or container applications separately. Learn more about sst dev.


Deploy

When you're ready to deploy your app, you can use the deploy command.

bash
sst deploy --stage production

Stages

The stage name is used to namespace different environments of your app. So you can create one for dev.

bash
sst deploy --stage dev

Or for a pull request.

bash
sst deploy --stage pr-123

Learn more about stages.


Console

Once you are ready to go to production, you can use the SST Console to auto-deploy your app, create preview environments, and monitor for any issues.

Learn more about the Console.


FAQ

Here are some questions that we frequently get.


Is SST open-source if it's based on Pulumi and Terraform?

SST uses Pulumi behind the scenes for the providers and the deployment engine. And Terraform's providers are bridged through Pulumi.

SST only relies on the open-source parts of Pulumi and Terraform. It does not require a Pulumi account and all the data about your app and its resources stay on your side.


How does SST compare to CDK for Terraform or Pulumi?

Both CDKTF and Pulumi allow you to define your infrastructure using a programming language like TypeScript. SST is also built on top of Pulumi. So you might wonder how SST compares to them and why you would use SST instead of them.

In a nutshell, SST is for developers, while CDKTF and Pulumi are primarily for DevOps engineers. There are 3 big things SST does for developers:

  1. Higher-level components

    SST's built-in components like Nextjs or Email make it easy for developers to add features to their app. You can use these without having to figure out how to work with the underlying Terraform resources.

  2. Linking resources

    SST makes it easy to link your infrastructure to your application and access them at runtime in your code.

  3. Dev mode

    Finally, SST features a unified local developer environment that deploys your app through a watcher, runs your functions Live, creates a tunnel to your VPC, starts your frontend and backend, all together.


How does SST make money?

While SST is open-source and free to use, we also have the Console that can auto-deploy your apps and monitor for any issues. It's optional and includes a free tier but it's a SaaS service. It's used by a large number of teams in our community, including ours.


Next steps

  1. Learn the SST basics
  2. Create your first SST app

Www Src Content Docs 404 (`www/src/content/docs/404.mdx`)


title: '404' template: splash pagefind: false hero: title: '404' tagline: Page not found. Check the URL and try again.


Www Src Content Docs About (`www/src/content/docs/about.mdx`)


title: About template: splash pagefind: false hero: title: "n/a"

import About from "../../components/About.astro";


Www Src Content Docs Blog Announcing Sst 1 0 Conf (`www/src/content/docs/blog/announcing-sst-1-0-conf.mdx`)


title: Announcing SST 1.0 Conf author: jay template: splash image: /social-cards/blog/sst-1-0-conf.png lastUpdated: 2022-05-05 pagefind: false

You might’ve heard about our v1 release. As SST enters into a new phase, we wanted to do something special for our community. We are putting together a virtual community event to commemorate the launch!

SST 1.0 Conf is a free virtual event happening on May 17th, 2022 at 9am PDT. Register for the conference now.

It’ll feature talks from the SST community about how they are using SST and serverless in their projects. Including folks from Comcast, The LEGO Group, Analog Devices, and Shell. We’ll also have a couple of live demos and workshops from the SST team.

We know you are all very busy and we don’t want to take up your entire day. So we decided to make the talks really short, around 10 minutes each. We hope you’ll be able to join us.

While most of the schedule has been finalized, we are looking for a couple more speakers. If you’d like to share something with the community, fill out this short form and we’ll get in touch with you!

We want to use this event as a way to bring our community together. To share what we’ve all learnt over the last year of using SST.

Next Steps

  • Head over to the SST 1.0 Conf site and register.
  • Join the SST Slack (if you haven’t done so already).
  • Share on Twitter or LinkedIn that you’ll be joining SST 1.0 Conf.

Psst! Check out the domain the conference website is hosted on. It might be a sign of things to come!


Www Src Content Docs Blog Aurora Serverless In V3 (`www/src/content/docs/blog/aurora-serverless-in-v3.mdx`)


template: splash title: Aurora Serverless in v3 author: jay lastUpdated: 2025-01-05 pagefind: false

import { Image } from 'astro:assets';
import { YouTube } from '@astro-community/astro-embed-youtube';

We are adding Aurora, a new component for Amazon Aurora Serverless v2. Recently, AWS announced that Aurora Serverless v2 can scale to 0 and auto-pause. This is good for dev or PR stages.

There are some differences between this and the Postgres RDS component. We talk about it here in this video.


Getting started

To get started, you can add the Aurora component to your app.

ts title="sst.config.ts"
const vpc = new sst.aws.Vpc("MyVpc");

const database = new sst.aws.Aurora("MyDatabase", {
  engine: "postgres",
  vpc
});

Read more about the Aurora component.


Scaling

By default, this has a min of 0 ACUs and a max of 4 ACUs.

An ACU or Aurora Capacity Unit is roughly equivalent to 2 GB of memory. So pick the minimum and maximum based on the baseline and peak memory usage of your app.

ts title="sst.config.ts" {3-6}
new sst.aws.Aurora("MyDatabase", {
  engine: "postgres",
  scaling: {
    min: "2 ACU",
    max: "128 ACU"
  },
  vpc
});

If you set a min of 0 ACUs, the database will be paused when there are no active connections in the pauseAfter specified time period.

Read more about the scaling config.


Dev mode

Aside from scaling to 0, you can also configure the Aurora component to not deploy the database in sst dev. Instead it can link to your locally running database, if you enable the dev prop.

ts title="sst.config.ts" {3-9}
new sst.aws.Aurora("MyDatabase", {
  engine: "postgres",
  dev: {
    username: "postgres",
    password: "password",
    database: "local",
    host: "localhost",
    port: 5432
  },
  vpc
});

Read more about the dev config.


Cost

Each ACU costs $0.12 per hour for both postgres and mysql engine. The storage costs $0.01 per GB per month for standard storage.

So if your database is constantly using 1GB of memory or 0.5 ACUs, then you are charged $0.12 x 0.5 x 24 x 30 or $43 per month. And add the storage costs to this as well.

If your database scales to 0 ACUs and is auto-paused, you are not charged for the ACUs.

Read more about the cost of using Aurora.


Examples

We also have a few examples that you can check out.


Www Src Content Docs Blog Auth (`www/src/content/docs/blog/auth.mdx`)


template: splash title: Auth author: jay pagefind: false image: /social-cards/blog/sst-auth.png lastUpdated: 2022-09-01

import { YouTube } from '@astro-community/astro-embed-youtube';

Today we are launching Auth β€” a modern lightweight authentication library for your SST apps.

With a simple set of configuration, it'll create a function that'll handle various authentication flows. You can then attach this function to your API and SST will help you manage the session tokens.

Auth is completely stateless, standards based, and free to use.

You can read more about it over on our docs.

Overview

Auth is made up of the following pieces:

  1. Auth β€” a construct that creates the necessary infrastructure.

    • The API routes to handle the authentication flows.
    • Securely generates a RSA public/private keypair to sign sessions.
    • Stores the RSA keypair as secrets in the app's Config.
  2. AuthHandler β€” a Lambda handler function that can handle authentication flows for various providers.

    • High level adapters for common providers like Google, GitHub, Twitch, etc.
    • OIDC and OAuth adapters that work with any compatible service.
    • A LinkAdapter to generate login links that can be sent over email or SMS.
    • Can be extended with custom adapters to support more complex workflows, like multi-tenant SSO.
  3. Session β€” a library for issuing and validating authentication sessions in your Lambda function code.

    • Implemented with stateless JWT tokens that are signed with the RSA keypairs mentioned above.
    • Support for passing tokens to the frontend via a cookie or the query string.
    • Full typesafety for issuing and validating sessions with the useSession hook.

Launch event

We hosted a launch livestream on YouTube where we did a demo and a deep dive into the internals.

The video is timestamped and here's roughly what we covered.

  1. Intro
  2. What is Auth?
  3. Setting up the construct
  4. Configuring the AuthHandler function
  5. Magic link adapter
  6. Issuing a session
  7. Validating a session
  8. Behind the scenes
  9. Outro

Get started

Follow the Setup section in our docs to get started.


Www Src Content Docs Blog Buildforce Is Creating The First Ever Career Platform For The Construction Trade With Sst (`www/src/content/docs/blog/buildforce-is-creating-the-first-ever-career-platform-for-the-construction-trade-with-sst.mdx`)


title: Buildforce & SST pagefind: false author: jay template: splash image: /social-cards/blog/case-study-buildforce.png lastUpdated: 2021-10-28

We are talking to Michael Orcutt, a co-founder of Buildforce about their experience building with SST and Seed.

About Buildforce

Buildforce is building the first ever career platform for people in the construction trades, starting with those in the electrical trade. Its end-to-end platform enables people in the construction trades to maintain consistent work at fair pay and with employee benefits with our construction partners, an arrangement that is out of reach for many in the construction industry today.

Since their launch in 2020, Buildforce has become the go-to partner helping dozens of the largest contractors across its focus geographies connect with this workforce.

Buildforce currently operates in the state of Texas, has raised $5.5M to date, and is growing 10% week over week in 2021. Michael Orcutt is one of the founders of the company and is leading the engineering efforts. He is also highly involved in product and design as they build their web apps, mobile apps, and marketing site.

The Challenge

The Buildforce team knew they wanted to build their applications using serverless due to the inherent benefits. They originally started out with Serverless Framework. But over time Serverless Framework became a headache. Everything from the deployment process to testing was cumbersome. "We felt we were slow, velocity was down as the speed of development wasn't great", says Michael. "We also found creating infrastructure in YAML challenging". They realized this just wouldn't work as they scaled the team.

Enter SST

Around 2 months ago the team came together and decided they needed to make a change. They had heard about SST and the SST Guide. They decided they needed to take a deeper look.

As they tried out SST, "We were blown away by the local development environment", says Michael. The entire team decided to move to SST.

"We were blown away by the local development environment."

They decided to start a new SST app from scratch. They spent some time testing SST and within a month they had moved over completely. The new setup Michael says is "Such a great experience. We are at least 1.5 to 2 times faster than before".

"We are at least 1.5 to 2 times faster than before."

They also use Seed for their deployment workflow. They typically branch from main, work locally, push to a feature branch. The feature branches get deployed through Seed and connects to dev resources. Finally they rebase with master and that deploys to production. The dev and prod environments are on separate AWS accounts.

Looking Ahead

Michael says they really need to grow the team. As more and more electricians are onboarded, they need to make sure every construction worker has a great experience and our operations team has the right tools to manage our workforce.

From an architecture perspective, their single web application will most likely be split into multiple SST apps; one for their electricians, one for the contractors, and one for the ops team. They will also be introducing ElasticSearch for better search functionality.

Thanks to SST their entire development setup works seamlessly, allowing them to focus on the needs of a fast growing business.

Learn More

Learn more about the job opportunities at Buildforce.


Read more about SST and get started today.


Www Src Content Docs Blog Config (`www/src/content/docs/blog/config.mdx`)


title: Config author: jay image: /social-cards/blog/sst-config.png template: splash pagefind: false lastUpdated: 2022-08-22

import { YouTube } from '@astro-community/astro-embed-youtube';

We are launching a set of tools to securely manage secrets and environment variables in your SST apps, called Config.

You can read more about it in detail over on our docs. The Config libraries include:

  1. Constructs to define them
    1. Config.Secret
    2. Config.Parameter
  2. CLI to set secrets sst secrets [action]
  3. Lambda helpers to fetch them @serverless-stack/node/config
    • Throws an error if they are not defined
    • Fetches them automatically at runtime
    • Provides typesafety and autocomplete

Behind the scenes, Secrets and Parameters are stored as AWS SSM Parameters in your AWS account. They are stored with the Standard Parameter type and Standard Throughput.

This makes Config free to use in your SST apps.

Launch event

We hosted a launch livestream on YouTube where we did a deep dive of the Config and its internals.

The video is timestamped and here's roughly what we covered.

  1. Intro
  2. Demo
  3. Deep Dive
    1. Deep dive into the Parameters code
    2. Parameter vs Lambda environment variables
    3. Deep dive into the Secrets code
    4. IAM permission for fetching secrets
    5. CLI command sst secrets
    6. Secrets fallback
  4. Q&A
    1. Q: What is the AWS cost of using Config?
    2. Q: What does the SSM path look like?
    3. Q: Managing secrets in my CI pipeline
    4. Q: Managing secrets across AWS accounts
    5. Q: Accessing Config inside or outside the handler
    6. Q: Would changing a secret require redeployment?
    7. Q: Using Config for tests
    8. Q: SSM vs Secret Manager
    9. Q: Export secrets to a .env file
    10. Q: Reference Config across multiple SST apps

Get started

To get started, define a secret in your stacks.

typescript
import { Config, StackContext } from "@serverless-stack/resources";

export default function SecretsStack({ stack }: StackContext) {
  const STRIPE_KEY = new Config.Secret(stack, "STRIPE_KEY");

  return { STRIPE_KEY };
}

Use the config option to pass the secret into the function.

typescript
import { use, Function, StackContext } as sst from "@serverless-stack/resources";
import SecretsStack from "./SecretsStack";

export default function MyStack({ stack }: StackContext) {
  const { STRIPE_KEY } = use(SecretsStack);

  new Function(stack, "MyFunction", {
    handler: "lambda.handler",
    config: [STRIPE_KEY],
  }
};

In your terminal, run the sst secrets command to set a value for the secret:

bash
$ npx sst secrets set STRIPE_KEY sk_test_abc123

Finally in your function code, use the @serverless-stack/node/config library to reference the secret value:

typescript
import { Config } from "@serverless-stack/node/config";

export const handler = async () => {
  console.log(Config.STRIPE_KEY);

  // ...
};

To learn more check out our docs.


Www Src Content Docs Blog Configure Autodeploy Workflow (`www/src/content/docs/blog/configure-autodeploy-workflow.mdx`)


template: splash title: Configure Autodeploy workflow author: jay pagefind: false lastUpdated: 2025-01-30

You can now completely control the build process when auto-deploying your app through the Console.

Background

After you've enabled Autodeploy, the Console will start a build process, check out your code, detect your package manager, install dependencies, and run sst deploy; all without needing a config.

However, there are times where you want to configure the build process. For example, to run your tests, or run some post-deploy script.

You can now do that with the new autodeploy.workflow prop.


Workflow

The autodeploy.workflow prop allows you to completely take over control of the build process and run any commands you want.

ts title="sst.config.ts" {6-11}
export default $config({
  app(input) { /* ... */ },
  async run() { /* ... */ },
  console: {
    autodeploy: {
      async workflow({ $, event }) {
        await $`npm i`;
        event.action === "removed"
          ? await $`npm sst remove`
          : await $`npm sst deploy`;
      }
    }
  }
});

The problem with CI/CD scripts has always been that they are bash commands embedded in a YAML config. This can be hard to write and maintain.

Even though the SST config is in TypeScript, it's still cumbersome to write shell scripts in Node.


Bun Shell

To address this we run the workflow function in a Bun process. $ is the Bun Shell, a bash-like shell that makes it easy to write scripts in TypeScript and JavaScript.

ts title="sst.config.ts"
async workflow({ $, event }) {
  await $`npm i pnpm`;
  await $`pnpm i`;

  const { exitCode } = await $`pnpm test`.nothrow();
  if (exitCode !== 0) {
    // Process the test report and then fail the build
    throw new Error("Failed to run tests");
  }

  event.action === "removed"
    ? await $`pnpm sst remove`
    : await $`pnpm sst deploy`;
}

This simplifies the shell commands in your deployment scripts.


Errors

In the above example, we are throwing an error after we handle a failed command.

ts
throw new Error("Failed to run tests");

This will fail the build and display this error message in the Console.


Learn more

You can see the new workflow prop in action in how the Console is deployed; since the Console auto-deploys itself!

ts title="sst.config.ts"
async workflow({ $, event }) {
  await $`bun i`;
  await $`goenv install 1.21.3 && goenv global 1.21.3`;
  await $`cd ../platform && ./scripts/build`;
  await $`bun i sst-linux-x64`;
  event.action === "removed"
    ? await $`bun sst remove`
    : await $`bun sst deploy`;
}

Learn more about the new workflow prop in the docs.


Other updates

We also rolled out a couple of other updates to Autodeploy:

  • We implemented a workaround so you won't get the annoying Docker Hub rate limit errors while deploying the Service component.
  • You can now return multiple stages in autodeploy.target. This means that when you git push to your repo, the Console can deploy to multiple stages.

You can learn more about the Console and Autodeploy in the docs.


Www Src Content Docs Blog Console Container Updates (`www/src/content/docs/blog/console-container-updates.mdx`)


template: splash title: Console container updates author: jay lastUpdated: 2024-11-25 pagefind: false

We are rolling out some updates to the Console to better support container applications.

Logs

You can now see logs from your containers in the Console. In addition to the logs from your functions. Head over to your App > Stage > Logs.

As a bonus, you can see logs from any CloudWatch log group that your app creates.

Autodeploy

The Console can auto-deploy your apps when you git push to your GitHub repo. We added the following updates recently.

  • VPC: You can now configure Autodeploy to run in your VPC using autodeploy.runner.vpc. This is useful if your builds need to access private resources.
  • Caching: You can now configure files or directories to be cached as a part of your build process with autodeploy.runner.cache. This can help speed up builds.
  • Extended timeouts: Autodeploys now have a maximum timeout of 36 hours. This is useful for apps that take a long time to build.
  • New runner sizes: There are new xlarge and 2xlarge sizes with around 70GB and 145GB of memory; helpful for large applications.

Check out the updated console.autodeploy config.

Other updates

While not strictly related to containers we also added support for:

  • Manual deploys: You can now manually trigger a deployment from App > Autodeploy > hitting the Deploy button. You can pass in a Git ref and the stage you want to deploy to.
  • Redeploys: You can now redeploy any past deploy through the Console.
  • Cancel deploys: You can also cancel a deploy that's in progress through the Console.

About Autodeploy

If you haven't used Autodeploy before, we designed it to be a better fit for SST apps when compared to alternatives like GitHub Actions or CircleCI.

  1. Easy to get started, supports branch and PR workflow out of the box.
  2. Configurable, customize your workflow through your sst.config.ts.
  3. Runs in your AWS account, builds are run in your AWS account and can use your VPC.

And it integrates with the rest of the SST Console. Learn more about Autodeploy to get started.


Www Src Content Docs Blog Console Pricing Update (`www/src/content/docs/blog/console-pricing-update.mdx`)


template: splash title: Console pricing update author: jay lastUpdated: 2025-01-23 pagefind: false

:::note[Update]
Feb 10, 2025: We added a way to handle PR and other epehemeral stages. See FAQ.
:::

Starting Feb 1, we'll be rolling out a new pricing model for the Console. Currently the pricing is based on the number of times the Lambda functions in your apps are invoked. Moving forward, it'll be based on the number of active resources in your apps.


Why the change?

When SST was first released, there was a big focus on serverless and Lambda functions. The Issues feature was also the focal point of the Console. So it made sense to tie the pricing to the number of Lambda function invocations in your apps.

Over the last few months, we added the ability to Autodeploy your apps. And added broader support for container services. So a Lambda specific metric doesn't really make sense anymore.

We think the number of active resources will be more representative of your usage.


How does it work?

At the start of every month, or billing cycle, the Console will keep track of the stages that are updated across your workspace, add up the resources in those stages, and apply the following rate.

Resources Rate per resource
First 2000 $0.086
2000+ $0.032

Free Tier: Workspaces with 350 active resources or fewer.


Examples

So for example, if you have the following number of active resources in your workspace:

Resources Cost per month
350 You are in the free tier, so you'll be charged $0.
500 You are above the free tier, so you'll be charged $0.086 x 500 = $43.
2500 You are in a higher tier, so $0.086 x 2000 + $0.032 x (2500 - 2000) = $188.

The count of the active resources in your workspace resets at the start of every billing cycle.


FAQ

  1. Do I need to use the Console to use SST?

    You don't need the Console to use SST. It compliments the CLI and has some features that help with managing your apps in production.

    That said, it is completely free to get started. You can create an account and invite your team, without having to add a credit card.

  2. I'm still trying out the Console. Can I continue using it?

    You can continue using the Console as a part of the free tier. If you go over the free tier, you won't be able to access the production or deployed stages.

    However, you can continue to access your personal stages. Just make sure you have sst dev running locally. Otherwise the Console won't be able to detect that it's a personal stage.

  3. What is an active resource?

    Resources are what SST creates in your cloud provider. This includes the resources created by both SST's built-in components, like Function, Nextjs, Bucket, and the ones created by any other Terraform/Pulumi provider.

    Some components, like Nextjs and StaticSite, create multiple resources. In general, the more complex the component, the more resources it'll create.

    You can see a full list of resources if you go to an app in your Console and navigate to a stage in it.

    For some context, the Console is itself a pretty large SST app and it has around 320 resources.

    A resource is considered active if the stage it belongs to has been updated during the billing cycle. However, if the only update to a stage was to remove it, the resources in that stage will not be counted as active.

  4. What about PR stages?

    A stage has to be around for at least 2 weeks before the resources in it are counted as active. So if a PR stage is created and removed within 2 weeks, they don't count.

    However, if you remove a stage and create a new one with the same name, it does not reset the 2 week initial period.

  5. Does this apply to SST v2 and v3 apps?

    Yes, it applies to any kind of SST app.

  6. What if I'm on the old plan?

    If you are on the old plan, you don't have to switch and you won't be automatically switched over either.

    You can go to the workspace settings and check out how much you'll be billed based on both the plans. To switch over, you can cancel your current plan and then subscribe to the new plan.

    At some point in the future, we'll remove the old plan. But there's no specific timeline for it yet.

  7. Are there any volume pricing options?

    Yes you can contact us and we can figure out a pricing plan that works for you.

Learn more about the new pricing in our docs.


If you've got any questions about the new pricing, or about the Console in general, feel free to get in touch.


Www Src Content Docs Blog Container Spot Capacity (`www/src/content/docs/blog/container-spot-capacity.mdx`)


template: splash title: Container Spot capacity author: jay lastUpdated: 2025-01-13 pagefind: false

import { YouTube } from "@astro-community/astro-embed-youtube";

We are adding support for using Spot instances when you create a Fargate service with the Cluster component through the capacity prop. Spot instances can be around 50% cheaper and we talk about how they work here.

Background

You can create container services with ECS and Fargate in SST with the Cluster component.

You are charged per hour of vCPU and GB of memory used. With our base config, this works out to around $12 per month.

Spot instances are spare capacity that AWS has and it's available at a discounted rate, around $6 per month.


Spot

You can enable this using the new capacity prop.

ts title="sst.config.ts" {8}
const cluster = new sst.aws.Cluster("MyCluster", { vpc });

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

You can also configure the % of regular Fargate vs Spot capacity you want to use.

ts title="sst.config.ts" {7}
capacity: {
  fargate: { weight: 1 },
  spot: { weight: 1 }
}

Learn more about the capacity prop.


Caveats

There are a couple of caveats.

  1. AWS may reclaim this capacity and turn off your service after a two-minute warning. This is rare, but it can happen.
  2. If there’s no spare capacity, you’ll get an error.

This makes Fargate Spot a good option for dev or PR environments.

ts title="sst.config.ts"
capacity: $app.stage === "production" ? undefined : "spot";

Get started

Get started by checking out the Fargate Spot capacity example.

You can also check out our container service quick starts.

These will help you get started with building container services.


Www Src Content Docs Blog Container Support (`www/src/content/docs/blog/container-support.mdx`)


template: splash title: Container support author: jay lastUpdated: 2024-11-08 pagefind: false

import { YouTube } from "@astro-community/astro-embed-youtube";

Historically, SST has primarily supported deploying serverless applications. But over the last month we've slowly expanded native support for containers on AWS.

This includes changes across the entire SST platform.


1. Components

There's a new family of components that'll help you build with containers.

  • Cluster & Service

    These help you deploy your containerized applications to AWS using ECS and Fargate.

    ts title="sst.config.ts"
    const cluster = new sst.aws.Cluster("MyCluster", { vpc });
    
    new sst.aws.Service("MyService", {
      cluster,
      loadBalancer: {
        ports: [{ listen: "80/http", forward: "3000/http" }],
      },
      dev: {
        command: "npm run dev",
      },
    });

    In addition to configuring ECS and Fargate, this also configures service discovery for your applications.

  • Vpc

    Container applications are usually deployed in a VPC. So this component makes it easy to create a VPC. And optionally add a bastion host or a NAT gateway.

    ts title="sst.config.ts"
    new sst.aws.Vpc("MyVpc", { bastion: true, nat: "managed" });
  • Postgres, Redis, & Efs

    While these components are not specifically for containers, they've been designed to work well with the above Cluster and Vpc components.


Cost

Unlike our serverless components, that are pay-per-use, these components have a more traditional pricing structure. We've taken special care to ensure that these components are as cost effective as possible to get started with. While still allowing you to scale with them.

Unfortunately, AWS' pricing pages for these services is not great. So the above components have a new Cost section in their docs. For example, here's what the cost of using the Vpc component looks like.

You can read more about what we've done here.


2. CLI

There are two big things we've done with our CLI to support containers.

  1. The dev prop allows you to run your application locally in a new tab in the sst dev multiplexer.

  2. The new sst tunnel command allows your local machine to connect to resources that've been deployed in a VPC. This is helpful because most of the container related components need a VPC. You can check it out in action here.


3. Console

The SST Console now shows you logs for your containers. And Autodeploy will support running in the same VPC as your app. This will allow your deploy process to have access to all the resources in your app.


Get started

We've updated all our tutorials to help you get started with the new containers.

The frontends now support deploying to both serverless and containers.


What's next

Over the next few weeks we'll extend support to other languages and frameworks. Like Rails, Laravel, Python, Elixir, Go, and more.


Www Src Content Docs Blog Create Sst (`www/src/content/docs/blog/create-sst.mdx`)


title: create sst author: jay template: splash lastUpdated: 2022-06-28 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

Today we are launching create sst β€” a CLI that helps you get started with SST. It creates a full-stack starter that you can use to build your serverless applications.

While serverless has been around for a few years, there isn't a Twelve-Factor App setup for it.

With create sst, we are trying to bake a lot of these principles into a starter that you can use right away. Simply run:

bash
npm init sst

This will bootstrap a full-stack serverless application with end to end type-safety:

  • RDS as the database (with an option to use DynamoDB)
  • GraphQL as the API
    • Pothos for building GraphQL schemas
  • React as the frontend

It also does a few other things that you can step through using a new tutorial we added in our docs.

Launch event

We livestreamed the create sst launch event. You can check it out on YouTube.

Here's roughly what we covered during the launch:

The video is timestamped, so you can jump ahead.

Looking ahead

With create sst we are planning to address most of the common concerns of application development; including handling authentication, managing secrets, writing tests, and more. As a team we are able to spend a lot more time on little details to get this setup in a place that makes your team as productive as possible. We'll also be open sourcing a codebase that we are working on internally that uses create sst. We'd like it to serve as an example of a real world production application.

While create sst helps you get started with SST easily, it's better thought of as an opinionated way to assemble the primitives that SST provides. You might want to tweak this setup or use a different one entirely.

We've got a lot more in store and we can't wait to share it with you all!


Www Src Content Docs Blog Doorvest Is Using Sst To Simplify Real Estate Investing (`www/src/content/docs/blog/doorvest-is-using-sst-to-simplify-real-estate-investing.mdx`)


template: splash title: Doorvest & SST author: jay cover: /social-cards/blog/case-study-doorvest.png lastUpdated: 2021-10-27 pagefind: false

In this post we are talking to Orlando Hui from Doorvest about how they are using SST and Seed to simplify real estate investing.

About Doorvest

Doorvest creates a modern way for people to own high-yield rental homes completely online. Doorvest gets to know a customer and their investment goals before identifying and buying a home on their behalf. It handles renovations and places a resident in it, then sells the home to the customer.

Doorvest is a VC backed company with 30 plus employees. Orlando Hui is the lead engineer on the team and was responsible for implementing SST across their stack.

The Challenge

While the original version of the Doorvest client was built using Serverless Framework, the dev workflow was really painful. "Deployments took 7 minutes", says Orlando. For most cases they had to deploy to test their changes. So the commit, deploy, feedback loop was something that they couldn't continue to use.

The original application also connected to resources that were not created in YAML and were created through the AWS Console. This was partly because it seemed easier to create them through the console, as opposed to working with the CloudFormation YAML that Serverless Framework uses.

Using SST

The Doorvest team had been following SST since it's Hacker News launch back in February, 2021. But they were unable to find an excuse to use it internally. Then a couple of months ago they needed to build an application for the general contractors on Doorvest. This allowed them to try out SST in production.

The team built everything from scratch, used the constructs in SST and CDK to define all their infrastructure as code. They also followed the best practices of separating their environments by AWS accounts. So each developer has their own AWS account, the staging and production environments are also in separate accounts. Their SST apps are deployed through Seed, and the combination of the two worked perfectly for them.

"SST doesn't have anything that's missing for us. We have everything we need.", says Orlando. "We don't have to do YAML anymore. The Live Lambda Dev is incredible."

"The Live Lambda Dev is incredible."

Comparing their workflow from before, Orlando thinks, "it's improved our productivity by at least 3 times".

"It's improved our productivity by at least 3 times."

He also found the SST Slack community while working on it. "The Slack group has been super incredible", says Orlando.

"The Slack group has been super incredible."

Recently, he was looking for WebSocket authorizer support and "it got built almost instantly after I brought it up".

Looking Ahead

"Now everybody on the team just wants to migrate away from Serverless Framework", says Orlando. As a part of their current sprint they are figuring out how to move over to SST completely.

The Doorvest engineering team is looking to grow 4x in 2022 and are actively seeking new engineers. They added 3 new folks recently and "having everybody use SST has been great, especially the new engineers".

Learn More

Learn more about the job opportunities at Doorvest and help them in their cause to simplify real eastate investing.


Read more about SST and get started today.


Www Src Content Docs Blog Frontends Are Hard (`www/src/content/docs/blog/frontends-are-hard.mdx`)


title: Frontends are hard template: splash author: jay lastUpdated: 2025-05-02 pagefind: false

import { Image } from "astro:assets"

import SpaDiagram from "../../../assets/blog/frontends-are-hard/spa-architecture-diagram.svg";
import SsrDiagram from "../../../assets/blog/frontends-are-hard/ssr-architecture-diagram.svg";
import MarketMap from "../../../assets/blog/frontends-are-hard/framework-market-map.svg";

import tweetDark from "../../../assets/blog/frontends-are-hard/cloudfront-announcement-tweet-dark.png";
import tweetLight from "../../../assets/blog/frontends-are-hard/cloudfront-announcement-tweet-light.png";

Modern frontends like Next.js, Svelte, Remix, Astro, etc. are hard to deploy. There are services like Netlify and Vercel that have built custom infrastructure just to do this.

In this post we look at why frontends have become so hard to deploy and why even the giant cloud providers like AWS, GCP, or Azure have really poor support.

We also look at how some recent changes have made it possible for SST to better support them.


Background

Back in the 2010s frontends were single-page apps. Hosting them was really easy. You just uploaded it to an S3 bucket and shared the URL with your users. To get fancy, you could add a CDN.

You could do this on any cloud provider. Like AWS or GCP, and it was the simplest part of your stack.

Modern frontends now need a combination of infrastructure like S3 buckets, serverless functions, databases, CDNs, edge functions, edge data stores, and more.

There are also a dozen or so competing frameworks.

This is complicated enough that even AWS and GCP's services like Amplify and Firebase have really poor support for them. They only support a couple of frameworks, don't support their latest versions, and don't cover all their features.

As a result, most people use secondary cloud providers like Netlify or Vercel. These services have their own custom infrastructure built on top of the major cloud providers. They've used this approach to grow to impressive scales. Both of these companies are reportedly doing around $100M in ARR.

But why do we need dedicated services? What's so hard about deploying a frontend? How come even the giant cloud providers are unable to keep up?


Why is it so hard?

There are a few main reasons why frontends are hard to deploy and host:

  1. Complicated infrastructure

    With single-page apps, it's all just static files. But modern frontends have evolved to support server-side rendering, API routes, image optimization, edge support, middleware, and a lot more. They are now closer to full-stack apps.

    Modern frontends are now closer to full-stack apps.

    This needs a combination of infrastructure like S3 buckets, serverless functions, databases, CDNs, edge functions, edge KV stores, etc.

    There is no one-size-fits-all piece of infrastructure for hosting a modern frontend.

  2. Dozen different frameworks

    From Next.js to Astro, SvelteKit, Remix, SolidStart, to recent ones like TanStack Start or React Router v7 in Framework mode. There are at least a dozen competing frameworks with different features or areas of focus.

    This leads AWS and GCP to pick which ones they support.

  3. Faster pace of updates

    All these frameworks are also constantly being updated.

    The big cloud providers are slow to release updates on their end. Meaning that the versions they support are almost always out of date.

Caveats

You could self-host most of these frameworks in a container, or self-host as a single-page app. But these modes of deployment have their limitations and are clearly sub-optimal compared to using a dedicated service.


Open source for the win

When we had first started building SST, deploying frontends was a small part of what we did. But as we grew, we started to get more requests for better frontend support.

Self-hosting frontends is a perfect fit for an OSS project.

We looked at the above problems; not having a one-size-fits-all solution and a long tail of frameworks and features. And realized that this was a perfect fit for an OSS project.

So here's what we did:

  1. Break down the problem

    Deploying a frontend is a combination of generating a build output through something called an adapter. And then using that to deploy the infrastructure.

    While the infrastructure is specific to the provider, the adapter could potentially be shared.

    So when we wanted to support frontends in SST, we decided to separate the two steps, and open source the adapters.

    This is led us to start the OpenNext project.

    Now providers like Cloudflare, and even Netlify and Vercel, can contribute to the project and help keep it up to date.

  2. Open source everything

    Aside from adapters, even the infrastructure SST uses to deploy frontends is open source. You can just look at our repo to see how we deploy them.

    This allows people to contribute and makes it so that SST supports a large number of frameworks, covers all their features, and remains up to date with new releases.

  3. Work closely with the framework authors

    Since everything is open source, it also makes it easier for us to collaborate with the framework authors directly.

This made SST the best available option for people that want to deploy their frontend to their infrastructure.


Good or good enough?

The infrastructure that SST creates when you self-host your frontend is a little different from what Netlify or Vercel creates.

It stems from the fact that Netlify and Vercel are multi-tenant and can share some bits of infrastructure across all their users.

Whereas SST creates a completely isolated setup for your frontend where nothing is shared between your sites and you are of course isolated from other SST users.

The downside of our approach is that we recreate all the infrastructure, like the CDN distribution, for each of your sites. This means it can take 15-20 mins to create one of these. So if you have multiple frontends or if you were creating preview environments, your deployments will be slower.

And somewhat related, SST didn't really support deploying your frontend to multiple regions.

We had been looking for ways to improve our setup. And now we can, thanks to this.


A new approach

A seemingly trivial pre:Invent announcement made CloudFront a lot more programmable. This paved the way for us to support setups that are far more flexible than Netlify or Vercel.

The key change here is that you can now use CloudFront Functions in tandem with a CloudFront KeyValueStore to modify the origin of requests. This lets you create custom policies for how traffic is routed from the CDN to your application.

In the past, you'd have to use a Lambda@Edge function and a DynamoDB table which is both a lot more expensive and a lot slower.


Introducing Router

We used this change to build the Router component. This component lets you use a single CloudFront distribution for your entire app.

ts title="sst.config.ts"
const router = new sst.aws.Router("MyRouter", {
  domain: {
    name: "example.com",
    aliases: ["*.example.com"]
  }
});

With it:

  1. You can set up routes to your frontends.

    ts title="sst.config.ts" {3}
    const web = new sst.aws.Nextjs("MyWeb", {
      router: {
        instance: router
      }
    });

    Or functions.

    ts title="sst.config.ts" {4,5}
    const api = new sst.aws.Function("MyApi", {
      url: true,
      router: {
        instance: router,
        path: "/api"
      }
    });

    Or S3 buckets.

    ts title="sst.config.ts" {5}
    const bucket = new sst.aws.Bucket("MyBucket", {
      access: "cloudfront"
    });
    
    router.routeBucket("/files", bucket);

    Or any URL.

    ts title="sst.config.ts"
    router.route("/external", "https://some-external-service.com");
  2. You can configure it to serve a subdomain.

    ts title="sst.config.ts" {4}
    new sst.aws.Nextjs("MyWeb", {
      router: {
        instance: router,
        domain: "docs.example.com"
      }
    });

    Or a path.

    ts title="sst.config.ts" {4}
    new sst.aws.Nextjs("MyWeb", {
      router: {
        instance: router,
        path: "/docs"
      }
    });

    For this, you'll also need to set the basePath in your next.config.js.

  3. You can share a router across stages. So your preview environments can deployed almost instantly.

    ts title="sst.config.ts"
    const router = $app.stage === "production"
      ? new sst.aws.Router("MyRouter", {
         domain: {
           name: "example.com",
           aliases: ["*.example.com"]
         }
       })
      : sst.aws.Router.get("MyRouter", "A2WQRGCYGTFB7Z");
  4. You can also deploy your frontends to multiple regions and it'll route requests to the server function that's nearest to your user.

    ts title="sst.config.ts" {2}
    new sst.aws.Nextjs("MyWeb", {
      regions: ["us-east-1", "eu-west-1"],
      router: {
        instance: router
      }
    });

How it works

The Router uses a CloudFront KeyValueStore to store the routing data and a CloudFront Function to route the request. As routes are added, the store is updated.

So when a request comes in, it does a lookup in the store and dynamically sets the origin based on the routing data. For frontends, that have their server functions deployed to multiple regions, it routes to the closest region based on the user's location.


Why this matters

Taking a step back for a second, a dedicated service like Netlify or Vercel is still likely to give you a better out-of-the-box experience.

Where SST shines, is when you start to grow. You start adding multiple frontends. You want serve your API from the same domain. Or you want your preview environments to include more than just your frontend.

This new setup allows you to do all of that. This is an example of one of SST's design principles. You can start with the simplest setup, without using a Router. Then grow to adding it. And eventually customize it. All through code.


Learn more

You can learn more about this new setup:


Final thoughts

There are also some broader questions like:

  • Why are there so many frameworks?
  • Why do these frameworks change all the time?
  • And do frontends need to be this complicated?

These are good questions that are worth having nuanced discussions about. We might tackle them in a future post. Or if you have some thoughts, we would love to hear them.


Www Src Content Docs Blog Go Runtime Support (`www/src/content/docs/blog/go-runtime-support.mdx`)


template: splash title: Go runtime support author: jay pagefind: false lastUpdated: 2024-12-20

import { YouTube } from '@astro-community/astro-embed-youtube';

SST v3 now supports the go runtime for your Lambda functions. Golang is a great option for tasks that are more compute intensive and it also has faster cold starts. We talk about it here:

You can use Go in SST by setting the runtime prop and pointing the handler to the directory with your Go function.

ts title="sst.config.ts"
new sst.aws.Function("MyFunction", {
  runtime: "go",
  link: [bucket],
  handler: "./src"
});

For more details, check out our Go example.


Also in this update:

  1. The sst dev CLI supports running your Go functions Live.

  2. You can use our new Go SDK to access linked resources in your Go functions or container applications.

    go title="src/main.go"
    import (
      "github.com/sst/sst/v3/sdk/golang/resource"
    )
    
    resource.Get("MyBucket", "name")

    The Go SDK does not support the clients that are available in the JS SDK.

  3. You can view Go function logs in the Console. Support for Issues is coming soon.


Www Src Content Docs Blog Henry Schein One The Worlds Largest Dental Practice Management Software Company Is Building With Sst (`www/src/content/docs/blog/henry-schein-one-the-worlds-largest-dental-practice-management-software-company-is-building-with-sst.mdx`)


template: splash title: Henry Schein One & SST author: jay image: /social-cards/blog/case-study-henry-schein-one.png lastUpdated: 2021-11-16 pagefind: false

In this post we are talking to Jack Fraser, a Software Engineering Manager at Henry Schein One. They are using SST and Seed to power the patient booking experience and the management software used by thousands of dental practices globally.

About Henry Schein One

Henry Schein One is the world’s largest dental practice management software company. Founded in 2018, Henry Schein One launched a new era of integrated dental technology by merging the market-leading practice management, patient communication and marketing systems of Henry Schein and Internet Brands into one company.

Henry Schein, Inc. (Nasdaq: HSIC), Henry Schein One's parent company, is the largest wholesaler of dental and medical products to office-based practitioners. The company has been established for approximately 90 years, with a presence in 32 countries to offer hundreds of thousands of products to customers globally. The company is a Fortune World's Most Admired Company and is ranked number one in its industry for social responsibility by Fortune magazine.

Jack Fraser is a Software Engineering Manager at Henry Schein One. His team is building out applications that'll power the patient booking experience and the management software for thousands of dental practices globally.

The Challenge

As more dental practices around the world start to rely on digital services to manage their practices and patient booking; the challenge is then to "build applications that are snappy and easy to use for patients", says Jack Fraser. Each practice brings in roughly 3000 patients and so they need to be able to scale rapidly as these will be rolled out globally.

They felt AWS made sense and serverless would help them with their scaling needs. So a year ago, Jack's team built out a serverless application in Serverless Framework. It had around 14 separate services.

However the experience wasn't great. The local development workflow was painful and using YAML to define the infrastructure didn't make as much sense.

In addition, their frontend applications were deployed as static sites to AWS through the AWS CLI. As Serverless Framework didn't have great support for this.

Switching to SST

This is around when they found SST and it made perfect sense for them. "It was based on CDK, and while CDK is great it is really verbose", says Jack.

"CDK is great but it is really verbose"

The other aspect that they found really appealing was the Live Lambda Development environment. "We loved the Live Lambda debugging in SST".

"We loved the Live Lambda debugging in SST"

Now they have 15 stacks in their SST app. With over 200 endpoints in their API. It also includes 4 Angular apps that use SST's StaticSite construct.

They also decided to move to GraphQL to manage their APIs. "We are already using SST's ApolloApi construct", says Phil Astle, a Senior Software Engineer on the team.

It's also all deployed through Seed. "We want to have a consistent release process", says Jack. Each developer on the team has 2 stages, a local one and a deployed one. There's also a sandbox environment and a production environment.

The PR workflow in Seed allows them to do code reviews and usability testing. It also allows them to spin up new environments to show changes to customers and get direct feedback.

Thanks to what SST and Seed offers, they decided to "make a big bet on SST", says Jack. And they've gone all in on SST.

"We have gone all-in on SST"

Looking Ahead

Jack's team is going to be creating multiple prod environments, one for a section of the practices for early rollouts and the second for the rest of their customers. They also need to setup multi-region deployments as they'll be entering new markets soon.

They are looking to double the team over the next couple of months. So Jack thinks that they need a dev environment thats "easy and intuitive". And a deployment workflow that just works. "We are a talent dense team, so it's important to have great tooling to be as productive as possible", says Jack.

Learn More

Learn about the job opportunities at Henry Schein One and join the world's largest dental practice management software company.


Read more about SST and get started today.


Www Src Content Docs Blog Is Serverless Ready Episode 1 (`www/src/content/docs/blog/is-serverless-ready-episode-1.mdx`)


template: splash title: Is serverless ready? lastUpdated: 2022-12-05 image: /social-cards/blog/is-serverless-ready.png author: jay pagefind: false

Today we are launching a new livestream series called β€” "Is serverless ready?", where we'll be showcasing the next generation of serverless tools and creators.

Over the next few weeks, we'll have speakers from Astro, Solid, Bun, PlanetScale, Mongo, SST, and more.

It's a chance for these tools and services to show off what they are working on and how they are making serverless ready.

So head over to isserverlessready.com and register. We'll send you an email when the next episode is coming out!


Www Src Content Docs Blog Issues Container Support (`www/src/content/docs/blog/issues-container-support.mdx`)


template: splash title: Issues container support author: jay lastUpdated: 2025-03-04 pagefind: false

import { Image } from "astro:assets"

import issues from '../../../assets/blog/sst-console-container-issues.png';

Issues now reports errors from Node.js container applications. Previously, only errors from Lambda functions were reported.


Reporting errors

For the Console to automatically report errors, you need to console.error an error object.

js title="src/index.ts"
console.error(new Error("my-error"));

In container applications, your code needs to also import the SST JS SDK.

js title="src/index.ts" {1}
import "sst";

console.error(new Error("my-error"));

This applies a polyfill to the console object to prepend the log lines with a marker that allows Issues to detect errors.

:::note
You'll need to update your SDK version to 3.9.24 or higher.
:::

If you are already importing the SDK, you won't need to add an additional import.


How it works

Issues works by adding a log subscriber to the CloudWatch Log groups in your SST apps. This has a filter that matches anything that looks like an error.

In the case of Lambda functions, the Lambda runtime automatically adds a marker to the logs that the filter matches for. For containers, the SST SDK polyfills the console object to add the marker.

Learn more about Issues.


Www Src Content Docs Blog Leadent Digital Is Transforming Field Service Operations With Sst (`www/src/content/docs/blog/leadent-digital-is-transforming-field-service-operations-with-sst.mdx`)


template: splash title: Leadent Digital & SST author: jay image: /social-cards/blog/case-study-leadent.png lastUpdated: 2021-11-11 pagefind: false

In this post we are talking to Ross Coundon, CTO of Leadent Digital about how they are using SST and Seed to transform field service operations.

About Leadent Digital

Leadent Digital provides the full spectrum of services from consulting advice to software development for field service operations. Their flagship product, the On My Way app provides real-time updates to customers on appointment visits, including expected time of arrival, appointment details and much more. Leadent's customers include Foxtel and Bosch Thermotechnology, and many others through a partnership with IFS.

Ross Coundon, is the CTO at Leadent Digital and leads their software development
efforts. Ross has been building software for over 20 years, and after years of building
on-premise software was looking for a way to focus on solving business problems and not have to manage any servers.

The Challenge

In their quest for reducing the operational overhead of building large applications, Ross
and his team experimented with AWS Elastic Beanstalk and Docker briefly before realizing that Lambda allowed them to build their MVPs far more easily and in a more cost effective
way.

Some of the very early serverless applications at Leadent were built using Claudia.js
and it was fine for a handful of Lambda functions. But it was hard to test in a real-world context.

This led them to Serverless Framework and serverless-offline. But there was "no
confidence in going from an emulated offline environment to production"
, says Ross. It
wasn't testing the permissions or any of the connections within the app. "You had to punt it over to AWS to find what was broken".

He also didn't think YAML was the right way to define infrastructure. He was looking for
type support and looked at using serverless.ts with Serverless Framework. But felt the
documentation was lacking, and for anything that was not directly supported by Serverless
Framework, he needed to rely on a combination of guesswork and the AWS docs to piece the two together.

This painful process of developing and testing led them to look at SST.

Enter SST

The constructs in SST made a lot of sense to Ross and his team. It allowed them to define all the infrastructure in one place and not have to use any YAML configuration. Better still, with full type support and IDE intellisense.

They were also impressed with the Live Lambda Development environment. "Knowing
that when I run an SST application I'm using real AWS infrastructure and my computer
is a part of the pipe was incredible"
, says Ross.

SST allowed them to set breakpoints in their code and debug in real-time, which meant that "you can iterate faster and build so much faster", says Ross.

"You can iterate faster and build so much faster."

The entire SST local development process was a revelation for the team. "I hadn't found anything like SST and haven't seen anything like it since", says Ross.

"I hadn't found anything like SST and haven't seen anything like it since."

They also use Seed to deploy their SST apps. All their major customers have separate deployments of their application. These are also deployed to separate AWS accounts. They use a PR based workflow in Seed and once their changes are merged to master, it gets deployed to all their customer accounts.

They just recently completed migrating their flagship product completely to SST. And
next week one of their larger customers will be using the new version of their
application.

The Future

The next 6 to 12 months is an exciting time for Ross and Leadent. They are looking to grow the team. They are also looking to expand the capabilities of their flagship product by adding live chat, so customers will be able to interact with the field service reps in real-time and they’ll be relying on SST for building this out.

Learn More

Learn more about Leadent Digital and their work here.


Read more about SST and get started today.


Www Src Content Docs Blog Learn To Build With Sst (`www/src/content/docs/blog/learn-to-build-with-sst.mdx`)


template: splash title: Learn to build with SST author: jay image: /social-cards/blog/learn-sst.png lastUpdated: 2022-09-12 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

We have a brand new tutorial that teaches you the basics of SST. And you can watch it as a video as well!

New Tutorial

You can check out the new tutorial here β€” docs.sst.dev/learn

This new tutorial uses a GraphQL starter, to build a simple Reddit clone. We go through the workflow of adding a new feature.

In addition to covering the basics of SST, this tutorial sets you up with all the other tools and packages you need to build a full-stack serverless application.

So if you are just getting started with SST, or serverless, or you want to learn how to build full-stack applications; this tutorial is for you!

Video Tutorial

We hosted a livestream on YouTube where we worked through the tutorial and talked about it in detail.

This will help if you want to follow along with the tutorial as a video.


We'll be putting out more "101" style videos to help you get started with SST. Make sure to subscribe to our YouTube channel to get notified!


Www Src Content Docs Blog Long Running Jobs (`www/src/content/docs/blog/long-running-jobs.mdx`)


template: splash title: Long running jobs author: jay image: assets/social-cards/sst-job.png lastUpdated: 2022-09-23 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

We launched a new construct that makes it easy to run functions longer than 15 minutes β€” Job

These are useful for cases where you are running async tasks like video processing, ETL, and ML. A Job can run for up to 8 hours.

Job is made up of:

  1. Job β€” a construct that creates the necessary infrastructure.
  2. JobHandler β€” a handler function that wraps around your function code in a typesafe way.
  3. Job.run β€” a helper function to invoke the job.

Launch event

We hosted a launch livestream where we demoed the new construct, did a deep dive, and answered some questions.

The video is timestamped and here's roughly what we covered.

  1. Intro
  2. Demo
  3. Deep Dive
    • Deep dive into the construct
    • Granting permissions for running the job
    • Typesafety
    • Defining the job handler
    • Running the job
    • Live debugging the job
  4. Q&A
    • Q: When should I use Job vs Function?
    • Q: Is Job a good fit for batch jobs?
    • Q: Why CodeBuild instead of Fargate?

Get started

Here's how you use the new Job construct. Start by creating a new job.

typescript
import { Job } from "@serverless-stack/resources";

const job = new Job(stack, "MyJob", {
  srcPath: "services",
  handler: "functions/myJob.handler",
});

Add the job handler.

typescript
import { JobHandler } from "@serverless-stack/node/job";

declare module "@serverless-stack/node/job" {
  export interface JobTypes {
    MyJob: {
      foo: string;
    };
  }
}

export const handler = JobHandler("MyJob", async (payload) => {
  // Do the job
});

Finally invoke the job.

typescript
import { Job } from "@serverless-stack/node/job";

function someFunction() {
  await Job.run("MyJob", {
    payload: {
      foo: "Hello World",
    },
  });
}

Note that the payload and job name MyJob here are typesafe.

For a full tutorial check out the Quick Start in the docs.


Www Src Content Docs Blog Moving Away From Cdk (`www/src/content/docs/blog/moving-away-from-cdk.mdx`)


title: Moving away from CDK template: splash author: jay lastUpdated: 2024-01-29 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';
import pulumiTweet from '../../../assets/blog/sst-pulumi-tweet.png';

You might've heard that we are working on a new version of SST (called Ion), that's not based on CDK. In this post we'll talk about why we are moving away from CDK and what's going to change.

You might've also seen the demo AI app we built with it β€” movies.sst.dev

This is going to be a fairly long post, so here's an outline of what we'll be covering.

  1. What is Ion
  2. What’s Wrong With CDK & CFN?
  3. Why The Change
  4. How Does Ion Work
  5. Roadmap
  6. What About SST v2
  7. What About Migrating
  8. What’s Our Goal
  9. FAQ

We also covered this post on a livestream.


What Is Ion?

Ion is a code name for a new engine for deploying SST applications. The constructs (or components) are defined using Terraform providers and deployed using Pulumi; as opposed to CDK and CloudFormation (CFN).

The components look roughly the same as they do today and it'll still be completely open source. But there are some differences. The biggest one being that you cannot use a CDK construct in Ion. You'll need to migrate it over.

Once Ion is stable, it'll be released as SST v3. This is a huge change, so let's look at why we are doing this.


What's Wrong With CDK & CFN?

If you've spent time in the SST community or if you've talked to other SST users, you've probably heard complaints about CDK or CloudFormation being slow. Or you've run into deployment failures and rollbacks. While these are annoying, is it enough to switch?

In the following sections I'll outline some of the underlying design flaws with the CDK & CFN model. I'll also talk about the practical implications of these problems and how some of these have turned into deal-breakers for us.

Design Flaws

Most of the problems with CDK & CFN stem from two specific design flaws. Let's start with the basics. This is what deploying a CDK (and SST) app roughly looks like.

txt
CDK Constructs > CloudFormation Templates > CloudFormation Deployment

Your CDK constructs generate a CloudFormation template (JSON), that is then sent to CloudFormation (the service) and it deploys your infrastructure. It's important to understand that this is a two step process.

The CloudFormation template defines what infrastructure you want to create. It does not describe how to create it. That is determined completely by AWS CloudFormation, the service.

1. CFN Is a Black Box

You give AWS CloudFormation a list of resources to create. It'll internally call the AWS SDK to create a resource. Then poll it until it's complete. It'll also maintain state of the resources it's managing.

Both the process it runs and the state it maintains are completely opaque and handled on the AWS side. CloudFormation does not run locally. You cannot customize it as a user and as we'll see later, neither can CDK.

CloudFormation is a black box that does not run locally.

As a framework author that's trying to build a better developer experience on AWS, this limits what we can do. And this turns out to be a deal-breaker. We'll look at why below. We'll also look at the alternative from the Terraform world.

2. CDK Is Not IaC, It's a CFN Hack

On the CDK side, we write TypeScript code that defines our infrastructure. But CDK doesn't create the infrastructure you define. It generates a CloudFormation template (JSON) that CloudFormation will use to create your infrastructure.

CDK doesn't create the infrastructure you define.

The distinction might seem subtle but it's a big reason for a lot of problems people run into. Let's look at an example with some CDK like code.

ts
const fn = new Function();

const dist = new CloudFront();

fn.addEnvironment({
  key: "endpoint",
  value: dist.url,
});

Here, CDK converts your function and CloudFront distribution definition into a CloudFormation JSON block for each resource, it fills in the unknown values (distribution URL that the function environment needs) with tokens that'll be resolved once the resources are deployed. It also uses this to figure out the order in which your resources need to be created.

This has two key implications:

  1. The order you write your code in, is not the order in which it'll be deployed. Here the distribution will be created first.
  2. Since this is converted into a large JSON object, you cannot do certain operations in your CDK code.

This odd behaviour makes a lot more sense when you realize that CDK is masquerading as a an IaC. When in reality it's just a CloudFormation hack.

One other place where this hack becomes apparent is in CDK's concept of an app. A CDK app is a collection of multiple stacks. CloudFormation doesn't have a concept of an app. So CDK has to hack your stacks together to make them seem like an app.

We'll look at the practical implications of this design flaw below. We'll also look at an alternative design that sidesteps all these issues.

Practical Problems

The above might sound like theoretical problems and you might be wondering how this impacts me as a user. Let's look at the practical implications of these design flaws.

The list below is not comprehensive but they make up most of our support cases.

1. Linking Resources

Say you have a Next.js app that shows a list of blog posts; where these posts are stored in a DynamoDB table. When your Next.js app builds, it'll generate this page by querying your database for these posts. So to generate your Next.js build, you'll need to deploy the table first. But CloudFormation needs you to provide the assets that your resources need (the Next.js site) to do a deploy.

Effectively meaning that you need to first deploy your database before deploying your Next.js site. This needs two deploys. It applies to any two resources where an asset for one depends on another (there's a Custom Resource hack that's used for cases like this but it won't work here and has its own problems).

This problem is a deal-breaker for users that are deploying to multiple environments. Since every time they create a new environment, they'll need to deploy their app in two steps.

You'll need to deploy your app in two steps, this makes it a deal-breaker.

Let's assume you are able to put up with this at your company. You'll run into a new problem. Let's say your Next.js site depends on the value of a resource that's set as a part of the deploy process. Something like a Config Parameter. SST needs to pull this value before the deploy, to build your Next.js site. But since this hasn't been re-deployed yet, it can only fetch its previously deployed value. This can be really hard to debug because it's not immediately apparent what's going on.

SST exists today because of two main reasons; Live Lambda & Resource Binding. And due to the CDK & CFN deployment model we cannot do Resource Binding well.

2. Cyclical Dependencies

There's another similar problem that's turning into a deal-breaker for our users. Recall that CDK isn't truly IaC, it's a JSON generator. This means that it'll generate JSON CloudFormation templates that won't work. A very common failure case is where some resources depend on each other. These are called cyclical dependencies. They come in two flavours.

Type 1: Resources

Similar to the example we used above, let's say you create a Lambda function with a function URL and you create a CloudFront distribution that points to the function's URL. And you want the function to have an environment variable with the CloudFront distribution URL.

ts
const fn = new Function();
const dist = new CloudFront({
  route: {
    "/my-function": fn.url,
  },
});

// You cannot do this
fn.addEnvironment({
  key: "endpoint",
  value: dist.url,
});

This looks perfectly fine in CDK code but cannot be expressed in a JSON CloudFormation template. This is because the three resources now reference one another. It's a cyclical dependency.

Unfortunately this comes up in our support pretty often. A solution for this is telling people to configure a custom domain so they know the URL before it's deployed. Or storing the distribution URL somewhere that can be later fetched inside the Lambda function. We have ways to make this easier but it requires top level await and that doesn't work in many environments and frameworks.

Type 2: Stacks

There's another version of this that's even more confusing. Let's say we are creating a queue and a function. The function needs to know the queue URL and the queue has a consumer that needs to know the function URL.

ts
const q = new SQS();
const fn = new Function({
  environment: {
    q: q.url,
  }
});

q.consumer = new Function({
  environment: {
    fn: fn.url,
  }
});

This isn't a cyclical dependency. The reasoning is subtle but related to the underlying resources that are being created. It looks the same as the one above in terms of CDK code but it's not the same when written in CloudFormation.

CloudFormation stacks have resource limits. Meaning that if you have more than 500 resources in a stack, you need to move them out to a new stack. This happens with larger teams. They'll need to refactor their stacks often.

Say you split this into two stacks. One for the functions:

ts
function FnStack() {
  const fn = new Function();

  return fn;
}

And one for the queues:

ts
function QStack() {
  const fn = use(FnStack);

  const q = new SQS();
  q.consumer = new Function({
    environment: {
      fn: fn.url,
    }
  });
  
  fn.addEnvironment({
    q: q.url,
  });
  
  return q;
}

When you deploy an app with these two stacks, you'll get a different cyclical dependency error. Here CloudFormation cannot deploy this because these stacks depend on each other.

A fix for this is to refactor it in a way that the queue stack can be deployed first.

ts
function QStack() {
  const q = new SQS();

  return q;
}

And then deploy the function stack.

ts
function FnStack() {
  const q = use(QStack);

  const fn = new Function({
    environment: {
      q: q.url,
    },
  });

  q.consumer = new Function({
    environment: {
      fn: fn.url,
    }
  });

  return fn;
}

Try and look at these two cases again. They look basically the same, yet one works and the other doesn't.

The reason for this is subtle. The addEnvironment call in the first case modifies an existing resource definition. While the q.consumer call adds a new resource definition. Something that's non-obvious from looking at the code.

CDK is leaking implementation details from CloudFormation.

The core problem is that CDK is leaking an implementation detail from CloudFormation. A sign that the system is poorly designed.

3. Export in Use

Like the cyclical dependency issue Export in use is another dreaded error. It usually looks like this.

txt
Export stackA:ExportsOutput****** cannot be deleted as it is in use by stackB

Let's say there is a Bucket in theΒ stackA, andΒ stackB references itsΒ bucket.bucketName. You now want to remove the bucket. But if you do, you'll run into the error above.

To fix this, you need to do two deploys. You'll need to create a fake export with bucket.bucketName in stackA, remove the reference in stackB, and deploy. Then remove the bucket and the fake export in stackA and deploy again.

This happens when the references between the stacks in your CDK app change. CDK makes it really easy to share references between stacks in your app. It's a hack on top of CloudFormation that makes exports easier to use. CloudFormation also doesn't have a concept of an app. So it doesn't know this export has been removed in a stack that is about to be deployed.

This error happens frequently and is incredibly non-obvious to people that are new to CDK. We realized that it's almost impossible to explain this through our docs or through support. So we built a feature in SST to auto-detect this and add the fake export for you.

CDK creates a hack on top of CloudFormation to make it easier to use. SST then adds a hack to unwind the damage caused by that hack to make CDK easier to use.

This specific error and its subsequent handling is a microcosm of the problem with the entire model. CDK creates a hack on top of CloudFormation to make it easier to use. SST then adds a hack to unwind the damage caused by that hack to make CDK easier to use.

4. Swallowing Errors

A consequence of CloudFormation being a black box is that it handles any errors internally. So you'll see the error from CloudFormation as opposed to the underlying error with the resource. This is the reason why many AWS deployment errors are cryptic and vague.

Here's an example.

txt
Error Message: 18:59:16 UTC-0500 CREATE_FAILED  AWS::Route53::RecordSetGroup DatabaseDNSRecord Invalid request

What's happening here is that there was an error within CloudFormation while creating the resource. But it swallows the real error and instead gives you the generic Invalid request error.

As a framework author, it's these types of things that drive you crazy.

The workaround for this is to look at AWS CloudTrail, their audit log service. It'll show you the original event and error message. As a framework author, it's these types of things that drive you crazy.

5. Custom Resource Hacks

So we know that CDK translates to CloudFormation that then gets executed. But what if you wanted some custom logic. For example, you have an S3 bucket that your users use for file uploads and you want to remove all the files in it when you remove your app.

You might think you can just write some CDK code that does this. But you cannot do this natively because it cannot be expressed as a CloudFormation JSON template. To fix this there's an escape hatch called a Custom Resource. It's a Lambda function that can execute as a part of the deployment process.

The reason it needs to be a Lambda function is because CloudFormation is a black box and it cannot run any local code. You'll need to package it up as a Lambda function.

Still, this sounds passable. Just run some extra scripts as a workaround. In practice though, Custom resources have their own set of problems; they are slow, have side effects, and when they fail it can be crippling. Unfortunately, the CDK community (and us included) need to rely on this for basically anything that isn't supported.

Hacking Multi-Region

Custom Resources get used for something seemingly trivial like emptying a bucket to something as crazy as multi-region deployments. CDK supports multi-region deployments. But it does this via a Custom Resource. The problem is that CloudFormation is a single-region service. There's no way to link stacks across regions. So when you deploy stacks across regions you'll rely on Custom Resources to share references across them.

You can see the design flaw with the CDK & CFN model here. CloudFormation is a black box and CDK has to create these hacks to work around it.

Orphan Log Groups

The Lambda functions in Custom Resources create Log Groups in CloudWatch that they write to. When you remove your app, you want to remove these Log Groups, but you can't completely remove them. If your Custom Resource tries to remove its Log Group, it'll recreate it after it finishes running. There's a similar issue with setting the log retention for Lambda functions.

Custom Resources are trying to create another deployment model on top of CloudFormation.

There are other problems with Custom Resources but the core issue is that these are hacking together another deployment model on top of CloudFormation. That's a huge red flag.

6. Rollback Hell

CloudFormation can sometimes run into issues when deploying a stack. When it does, it'll rollback all the changes it makes. This sounds good but it has two problems:

  1. It's really slow. CloudFormation deployments in general take very long and when they try to roll back, they take even longer.
  2. Rollbacks can fail and get stuck because some resources might fail to rollback. If this happens you are in rollback hell. You won't be able to redeploy the stack. You'll need to go to the AWS Console and try to redo the rollback. If it still fails, you'll need to specifically pick the resources that are failing to rollback and tell it not to rollback.

This is a pretty serious problem and we've gotten plenty of panicked support messages from teams that get stuck in this when they are deploying a big update to prod. People complained about this enough that CloudFormation added a setting to skip rollbacks in the case of failures.

However, we tried this as a default with SST and immediately we had a bunch of users get into states that they couldn't get out of. We were forced to revert the change.

Fun fact, if a Custom Resource has some faulty code you are guaranteed to be stuck in rollback hell. This is because on deploy, the faulty resource will fail, then it'll try to rollback the stack, and the faulty resource will run again and fail again.

7. Double Builds

CDK will sometimes do a lookup for a resource and save that information in a cdk.context.json. This file is meant to be committed and acts as a cache. If the cache doesn't exist, it'll build your app to create that cache, then build again while using the cached values. You need to commit this file to your Git repo.

However, if your deploys to prod are run in a CI environment and prod happens to be a separate AWS account, it'll always do a double build. As a developer on the team, you might not have access to prod from your local machine, meaning that you can't generate this file locally and commit it. Or commit the changes when it needs to be updated.

CDK tries to maintain its own state on top of CloudFormation.

This again stems from the design flaws of the system. CloudFormation maintains state internally for your deployed resources. But since it's a black box, CDK tries to maintain its own state on top of it.

8. Stack Resource Limits

As mentioned above, there's a resource limit of 500 for each CloudFormation stack. Say you have a Next.js site and a DynamoDB table, you might think that's 2 resources. But resources are counted in terms of the low level infrastructure that AWS needs to create these. The Next.js site creates around 68 resources. So you end up hitting this pretty quickly.

Once you hit this limit, you'll need to split resources into multiple stacks. But that process is not intuitive. You'll likely run into the cyclical dependency issue that we mentioned above.

9. Orphan Stacks

When you are trying to refactor your stacks, you'll be tempted to rename a stack. If you do that, CDK will just recreate all the resources in that stack with new names. And the old stack and its resources will be orphaned.

People run into this gotcha all the time. The right approach here is to delete the old stack and create a new one. But what if you have resources that cannot be removed; like your database. Well you can't change the stack name. You'll need to tell CDK not to generate a new stack name and continue to use the old stack name. Your infrastructure definition will now forever have this line in it.

Unless you reimport the resources into your new stack, which is not easy as we'll see.

10. Importing Resources

CloudFormation keeps state internally of the resources its managing in a stack. But because this is not available locally, it introduces some problems. The biggest one is importing resources. As in, adding a previously created resource to a CloudFormation stack. SST users try to do this frequently because they need to link to previously created resources in their app.

In theory an import should be simple. Just tell CloudFormation about a new resource and it'll manage it moving forward. In practice though it's extremely painful. You need to mock the resource in your current app, generate the CloudFormation template for your app, remove any extra info from the template, go to the CloudFormation console and ask it to import resources, set a unique ID for the resource, run a diff to see if your local stack is similar to the version that CloudFormation has, and finally do a drift check to make sure they are in sync.

In short, most people don't do this. This hurts SST as a framework, as it limits how effective it can be for an existing AWS user.

11. Slowness

I won't spend too much time here but for a myriad of reasons CDK and CloudFormation are extremely slow. There is slow, as-in something is noticeably slow. And then there is CloudFormation slow. It's behaviour altering. This was maybe acceptable for a world where you bring up servers and clusters, those are expected to be slow. But if you are dealing with managed services, it really shouldn't be this slow.

There is slow, as-in something is noticeably slow. And then there is "CloudFormation slow".

What's frustrating to us as framework authors is that we have no control over this. CloudFormation arbitrarily waits for resources to get updated because they optimize for the lowest common denominator use case. Imagine migrating from something like Vercel and waiting for 30 minutes for your first SST deployment.


Why The Change

We've looked at all the problems that we are facing with the current CDK & CFN model. But these are not new issues. So why did we decide to make a change now?

Background

Before we get into that, let me give you some background. We've worked on SST for 3 years now. You may also know us as the creators of Seed β€” a git push to deploy service for serverless apps. We started Seed in 2017. Prior to it supporting SST, Seed was exclusively for Serverless Framework apps. We've seen the ups and downs of Serverless Framework and their misstep with the Serverless Components idea.

If you are not familiar with that story, I'll give you the gist of it. Serverless Framework uses CloudFormation to deploy your serverless apps. At some point they decided to launch their own infrastructure deployment service, called Serverless Components. It was not based on CloudFormation or Terraform. It would run deployments through their servers (though there was a way to opt out of that). Long story short, it did not work.

At this point you are probably wondering if we are just repeating that mistake. Having lived through that it took us a really long time to consider options outside CDK. The problems listed above, are not new, we've known them for the better part of 2 years. We've also played around with Terraform versions of SST in the past.

There's also…

Supporting Non-AWS Providers

Back in 2017, AWS had a complete stranglehold on the serverless space. And rightfully so. Today in 2024, that is less true. Other providers now have services that are better than the AWS versions. Most notably Cloudflare. Slowly but steadily they've gotten better. To the point that there is now genuine demand from the SST community wanting to use Cloudflare. We are also seeing companies that are using serverless to build their applications and they are completely not on AWS.

To do this with the current CDK & CFN model we'd have to create Custom Resources for these providers. And as we have seen above, the Custom Resource hack is not worth it. That effectively means we'd have to go outside this model.

We've known in the back of our minds that at some point we were going to have to do this. However this was going to be such a big change that we basically avoided thinking about it too hard.

What Changed

Then something shifted over the last year, we launched OpenNext. Suddenly SST became the best way to deploy not just Next.js, but Remix, Astro, SvelteKit, and more to your AWS account.

Now some of the largest Next.js sites in the world are deployed through SST. The resource linking problem that we described up top went from a fringe concern to something that I personally have to deal with in support every single week.

Added to that we had to ask ourselves, if we weren't limited to the CDK & CFN model could we potentially build a better Next.js experience than Vercel. And we realized that we could. Vercel has to run some complicated infrastructure to support their multi-tenant use case. SST doesn't. Next.js apps deployed with SST are not just cheaper (Vercel's enterprise pricing is absurd) but also faster.

Working With Pulumi

Also this happened.

We decided to build a prototype a couple of months ago. We worked with the Pulumi team and founders closely to understand how their system works. We figured out how we could create a fully open source deployment experience with the Pulumi deployment engine and their bridged Terraform providers.

You simply install the SST CLI globally, create an sst.config.ts, and run sst deploy. There's nothing else to install, there are no node_modules, no CDK version conflicts, no services to sign up for, and no black boxes. Everything runs on your local machine.


How Does Ion Work

In Ion you define your infrastructure almost exactly like you do in SST today. Except instead of using CDK underneath it uses a TS version of the underlying Terraform provider. Note that Terraform providers are written in Go and are basically calls to the AWS SDK. There's some additional code to make sure these run safely, so you aren't hammering AWS.

When you run sst deploy, those resources make a call to an embedded Pulumi engine that makes a call to their bridged Terraform provider, and this makes the AWS SDK calls to create your resources. A bridged Terraform provider is a Pulumi provider that's programmatically bridged to the underlying Terraform Go provider.

If you are familiar with Terraform you might be wondering, isn't Terraform like CloudFormation templates in that you write JSON or HCL. When you deploy these templates through the Terraform engine, it translates these to calls to the Terraform Go providers for those resources. In Ion's case we don't use the Terraform engine, we use Pulumi.

You don't need to install Pulumi or Terraform or sign up for anything.

You don't need to install Pulumi or sign up for it, their engine is embedded in the Ion CLI. Your deployments create a state file locally that are synced with an S3 bucket in your AWS account.

There are a couple of key things with this model that pretty much addresses all the above issues.

  1. The code is executed exactly how you write it. The deployment engine is run from your local machine and it creates the resources directly. There is no layer or service in between.
  2. This simple execution order means there are no cyclical dependencies. If you have a linked resource, you need to create it in the order it is used.
  3. If you want to do something like the CloudFront and Lambda function example, where you want to set the distribution URL back into the function environment, you'll need to create a dynamic provider, this is basically code that's going to run locally after those resources have both been created. Again, the code you write is what's being executed and deployed. It's Infrastructure as Code.
  4. Any deployment errors are displayed directly and in full.
  5. Importing resources is a lot easier because you have visibility and control over the state of your deployment.

The code you write is what's being executed and deployed. It's Infrastructure as Code.

This model isn't all perfect. Over the last couple of months we've had to work around some issues. But we have a couple of key tools at our disposal.

  1. We can submit PRs to Terraform providers, since these are open source.
  2. There is a process of patching Terraform providers if the changes in the PR are urgent.
  3. We can also fork the provider if necessary and create our own.
  4. We talk to the Pulumi team regularly and can work with them to find fixes or work arounds.

The upshot is that we are not dealing with the CloudFormation black box. So we can ultimately do something about the problems we run into.


Roadmap

Let's look at where we are currently and what we've done for Ion so far.

Step 0: Ion Prototype Demo

Status: Complete

  • Created a prototype by embedding the Pulumi engine
  • Created a repo for Ion on GitHub
  • Implemented the most complicated SST construct in Ion β€” Next.js
  • Implemented a few other components, including a new Vector component based on Amazon Bedrock
  • Built a complete full-stack demo application with Ion β€” movies.sst.dev

You can view the repo for our demo movies app on GitHub. The entire SST team worked on this app and it was great watching all the CDK issues we listed basically vanish.

Step 1: Ion General Release

Timeline: Couple of weeks from now

Here's what we need to do next for a general release.

  • Implement a few more components and CLI features
  • Write up the docs
  • Do a lot of testing
  • Launch a new site β€” ion.sst.dev

At this point you'll be able to create new apps from scratch in Ion. It'll give you a chance to try it out. We'll be directing new users that want to get started with SST to start with Ion first.

Step 2: Ion β†’ SST v3

Timeline: Couple of months from now

Once Ion is stable, it'll give current SST users an opportunity to migrate their apps to Ion. We'll create some tools and documentation that'll help with this process.

Once we feel comfortable with it and we know many people will be able to move over, we'll set a date for the SST v3 release. This is when ion.sst.dev will become sst.dev and it'll become SST v3. While SST v2's docs will be moved to a separate site.


What About SST v2

So what happens to SST v2 once Ion becomes SST v3? Here's what we are planning:

  • We'll release updates
  • We'll accept PRs
  • We won't add new constructs
  • We won't introduce breaking changes

There might be caveats here of course but we are planning to let the community drive a lot of what happens with SST v2.


What About Migrating

Ion is obviously a big change. If you are using mostly SST constructs in your apps, the migration won't be a big problem.

But what if you are using a ton of CDK constructs currently in your SST apps? There are two types of these:

  1. L1s or low level CDK constructs

    This is far easier to move over because the Terraform AWS providers have support for these.

  2. High level CDK constructs

    But CDK is filled with a ton of high level constructs, especially ones that use the Custom Resource hack described above to make changes that are not in CloudFormation (and also not in Terraform).

The latter tends to be really messy. There is no way to sugar coat this. Migrating those over is not going to be easy. We'd argue that the new Ion model is better but there are no drop-in replacements for those. You could leave your old SST app around with those resources and not move them over. They'll continue to work because SST v2 isn't going anywhere.

We'll share more details on the migrations steps through the Ion docs. But in the long run you might have to make a choice.

Is Ion Right For Me

That brings me to something we should address directly. If your company is completely invested in the CDK & CFN model and you were using SST because of its strong ties to CDK; Ion is probably not going to be right for you. We'd still invite you to give it a try and build something non-trivial with it. But we know some folks are married to CDK.

In this case, the solution would be to migrate your SST apps to CDK when you can. At the end of the day, SST v2 is open source and built on CDK. We chose this architecture because we did not want you to be locked in to us in case something happened.

What Ion Is Not

It's also worth mentioning what we are not trying to be. We are not trying to be an extension of CDK or Terraform or Pulumi. We are not AWS purists.

So if you are choosing SST or Ion because it's basically CDK or Terraform but nicer, you might end up disappointed. SST is fundamentally trying to create something that didn't exist before. We've discovered many of our design patterns as a result of our personal experience with building products and working with teams that are doing the same.


What's Our Goal

So then what is our goal? Why are we doing all of this?

We want SST to be the best way to deploy an application. Let's take an example. This sst.config.ts is based on the movies demo from above. It includes AWS, OpenAI, and Cloudflare as components.

ts
const db = new aws.dynamodb.Table("Movies");

const bucket = new sst.Bucket("Assets");

const vector = new sst.Vector("Vector", {
  openAiApiKey: new sst.Secret("OpenAiApiKey").value,
});

const site = new sst.Nextjs("Web", {
  link: [db, bucket, vector],
});

new cloudflare.cdn("Cdn", {
  domain: "movies.sst.dev",
  route: {
    "/": site.url,
  },
});

We want you to be able to drop a single type-safe config file in your repo root, and run sst deploy. This should be able to create any kind of resource, in any cloud provider, and link them all together. We also want you to be able to customize any of this to the degree that the provider will let you.

Drop a type-safe config file and deploy it with a single command.

We want the best possible developer experience for this idea. And to accomplish it we need to bet on tools that we think will get us there.


FAQ

I'm sure you have a lot of questions. I'm going to try and add to this section as I address more of them.

1. Is Ion completely open source and self hosted?

Yes it's open source and it'll run on your local machine without you having to sign up for anything Pulumi or Terraform related.

2. Our company only uses AWS, can we still use Ion?

Yes. While Ion will support multiple providers, if you are using AWS it will deploy completely to your AWS account. There won't be any other services or providers involved outside of the one you choose.

3. Isn't Terraform not open source anymore?

There's been some recent fallout from the Terraform licensing change but that applies to the Terraform engine itself. Ion doesn't use this.

4. What happens to the SST Console?

It'll support Ion and SST v2 apps. We have some plans for making it easy to self-host the Console. We'll share more details once we launch Ion.


Www Src Content Docs Blog Moving To Discord (`www/src/content/docs/blog/moving-to-discord.mdx`)


template: splash title: Moving to Discord author: jay lastUpdated: 2022-06-23 pagefind: false

We are moving our Slack community to Discord! While our Slack community has grown to over 2000 members, we feel that Discord is better aligned with supporting open source communities.

To join our Discord server, head over to: discord.gg/sst

If you are curious about why we are moving to Discord, I shared some thoughts on this on Twitter.

We hope to see you in Discord soon!

PS: The Slack community will be around while we transition.


Www Src Content Docs Blog New Console Logs Ui (`www/src/content/docs/blog/new-console-logs-ui.mdx`)


template: splash title: New Console Logs UI author: jay lastUpdated: 2025-02-18 pagefind: false

import { Image } from "astro:assets"

import logsLight from '../../../assets/blog/sst-console-logs-light.png';
import logsDark from '../../../assets/blog/sst-console-logs-dark.png';

We recently updated the logs UI in the SST Console. We've added the ability to search, view log streams, and just made it faster.

In the Console, you can view logs from your functions, containers, or any other CloudWatch log groups. We added the ability to:

  • Search these logs for any string. You can also set a search filter and tail the logs. This helps you debug any issues by triggering your app and watching for the generated logs
  • View log streams. From the search results, you can jump to the log stream that the log message is coming from. Typically, for very active log groups, you'll see logs from multiple log streams interleaved together. When you view a specific log stream, you can drill down and just view the logs from that specific invocation or request.
  • Faster logs. We've also used one of the newer CloudWatch APIs that makes fetching these logs from your account a lot faster.

Check out the new logs in the Console.


Www Src Content Docs Blog New Sst Console (`www/src/content/docs/blog/new-sst-console.mdx`)


template: splash title: New SST Console author: jay lastUpdated: 2023-09-22 pagefind: false

The new SST Console is out. You can learn more about it over on our docs.

What is the SST Console

The SST Console is a web based dashboard to manage your SST apps. With it you can invoke functions, debug issues, view logs, and manage all your apps with your team β€” console.sst.dev

Features

Logs

With the SST Console, you don't need to go to CloudWatch to look at the logs for your functions. It also supports a couple of modes; recent, live, and time intervals.

Issues

The SST Console will automatically show you any errors in your Lambda functions in real-time. There is:

  • Nothing to setup, no code to instrument
  • Source maps are supported automatically, no need to upload
  • No impact on performance or cold starts, since the functions aren't modified

Local logs

When the Console starts up, it checks if you are running sst dev locally. If so, then it'll show you real-time logs from your local terminal.

Open source

The Console is built with SST, deployed with Seed, and you can view the source on GitHub.

The codebase is also a good example of what a production SST app looks like.

Your team and AWS accounts

You can create a workspace in the Console, invite your team, and connect all your AWS accounts. It'll automatically discover your SST apps.

Pricing

The SST Console pricing is based on the number of times the Lambda functions in your SST apps are invoked per month. It comes with a free tier of 1M invocations.

You can read more about it over on the docs.

Old Console

The old SST Console is still accessible at old.console.sst.dev. But we'll be moving away from it in the future.

Learn more

Learn more about the new SST Console. And if you have any questions, join us over on Discord.


Www Src Content Docs Blog Open Next V1 (`www/src/content/docs/blog/open-next-v1.mdx`)


title: OpenNext 1.0 template: splash author: jay lastUpdated: 2023-04-17 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

OpenNext 1.0 is out. You can try it now with the NextjsSite construct.

What is OpenNext

OpenNext is an open-source Next.js serverless adapter created by the SST team. It allows you to self-host Next.js serverlessly.

To celebrate the 1.0 release, we created a fun video.

Background

Next.js does not support self-hosting your app using serverless. There have been several projects to try and fix this. However, this is not easy to do, as it requires reverse engineering how Vercel deploys Next.js internally. As a result most of these attempts have failed or have ended up as closed source paid implementations.

The goal of OpenNext is to create a free open source, framework agnostic, serverless adapter for Next.js.

OpenNext 1.0

OpenNext was created back in December, 2022. Since then the repo has grown to over 1000 stars on GitHub. The community has really come together to ensure that OpenNext supports all of Next.js 13's features. The OpenNext channel is also one of the most active channels on our Discord.

While OpenNext is already being used in production, the 1.0 release marks a significant milestone in terms of stability and performance.

We'd love for you to take it for a spin.

Get started

Here's how you can use OpenNext to deploy your Next.js app to AWS with SST.

bash
npx create-next-app
npx create-sst
npx sst deploy

Check out the full tutorial. If you are using a different framework, check out the other deployment options.


We could use your support in maintaining OpenNext, make sure to join #open-next on Discord and star us on GitHub.


Www Src Content Docs Blog Openauth Beta (`www/src/content/docs/blog/openauth-beta.mdx`)


template: splash title: OpenAuth Beta author: jay pagefind: false lastUpdated: 2024-12-09

import { YouTube } from '@astro-community/astro-embed-youtube';

Today we are launching OpenAuth β€” openauth.js.org

A universal, standards-based auth provider. It's in beta along with the new Auth component for SST v3 that uses this.


What is OpenAuth

OpenAuth is a new auth provider that's built on top of OAuth 2.0. It's a single-page app that you can deploy anywhere.

  • Universal: You can deploy it as a standalone service or embed it into an existing application. It works with any framework or platform.
  • Self-hosted: It runs entirely on your infrastructure and can be deployed on Node.js, Bun, AWS Lambda, or Cloudflare Workers.
  • Standards-based: It implements the OAuth 2.0 spec and is based on web standards. So any OAuth client can use it.
  • Customizable: It comes with prebuilt themeable UI that you can customize or opt out of.

You can learn more about it over on the OpenAuth GitHub.


Why build OpenAuth

Auth has been one of the most requested features in SST. With v1 and v2 of our Auth components, we found that most folks wanted a standalone, easy to self-host auth provider.

Most of the existing solutions were either libraries, or SaaS services that held your user data. And the standalone self-hosted ones were just too complicated to set up.

We also think a universal, standards-based auth provider is something that should exist outside of SST, which is why we built OpenAuth as a separate project.


Using it in SST

OpenAuth needs some minimal infrastructure to run. The new Auth component in SST v3 can do this for you.

ts title="sst.config.ts"
new sst.aws.Auth("MyAuth", {
  domain: "auth.myapp.com",
  authorizer: "src/auth.handler"
});

Next steps

OpenAuth and the new Auth component are in beta. Over the next few weeks we'll be adding more docs, examples, and tutorials, as we finalize the API.

If you are eager to try it out and play around, head over to the OpenAuth README and check out the examples.

You can star the repo and follow us on X as we get closer to a 1.0 release.


Www Src Content Docs Blog Opennext Community (`www/src/content/docs/blog/opennext-community.mdx`)


template: splash title: OpenNext community author: jay lastUpdated: 2024-09-24 pagefind: false

We launched OpenNext back in April 2023 to build an open-source adapter for Next.js.

Thanks to our community, most notable conico974 and khuezy, it's now the best way to deploy your Next.js apps on AWS.

Today OpenNext is joined by Cloudflare and Netlify; making it a broader community effort. They'll be publishing their adapters through OpenNext as well.

You can support the community by:

It's been great to see the community take shape around the project.


Www Src Content Docs Blog Remixsite Construct (`www/src/content/docs/blog/remixsite-construct.mdx`)


title: RemixSite author: jay image: /social-cards/blog/remixsite-construct.png template: splash lastUpdated: 2022-07-19 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

The new RemixSite construct makes it easy to deploy Remix apps to AWS.

Remix is a frontend focused framework that delivers modern web experiences but by relying on web standards. The SST community has been looking for a way to deploy a Remix app with SST.

Recently, Sean Matheson put together a very comprehensive PR implementing an SST construct that would deploy a Remix app.

Frank made a couple of changes and merged the PR to officially add the RemixSite construct to the SST family of constructs.

It allows you to:

  • Access other AWS resources in your Remix loaders and actions.
  • Reference environment variables from your backend.
  • Easily set custom domains.
  • Host your Remix apps in single-region or on the Edge.

Launch event

We hosted a launch livestream on YouTube where we did a deep dive of the construct and its features.

We also, took a look at the internal architecture and the construct code. Here's roughly what we covered.

  • Intro
  • Architecture
  • Environment variables
  • Access AWS services
  • Custom domains
  • Deploy to Edge
  • How to get started
  • Deep dive into the code
  • How does it work with sst start?
  • Why is the non-Edge setup the default?
  • Should we be talk to the DB directly?
  • Does the app size impact deploy time?
  • What about the cache policy limit?
  • Which Remix deployment target to use?
  • Q&A

The video is timestamped, so you can jump ahead.

Get started

You can get started with the RemixSite construct by taking our example for a spin:

bash
$ npx create-sst@latest --template=examples/remix-app my-sst-app
$ cd my-sst-app
$ npm install
$ npx sst start

# Deploy to prod
$ npx sst deploy --stage prod

Also make sure to check out the docs.


Www Src Content Docs Blog Serverless Stack Raises 1m To Make It Easy To Build Serverless Apps (`www/src/content/docs/blog/serverless-stack-raises-1m-to-make-it-easy-to-build-serverless-apps.mdx`)


title: SST raises $1M template: splash author: jay lastUpdated: 2021-07-23 pagefind: false

Today we are announcing our $1M seed round. The new funding helps us in our mission to make it easy for developers to build full-stack serverless applications. You can read more about this announcement over on TechCrunch as well.

Our SST allows developers to test and make changes to their applications by directly connecting their local machines to the cloud. The biggest issue that developers face while adopting serverless is the ability to debug their applications locally. With SST, developers are able to set breakpoints and inspect their code, just as they would with any other type of application. Since its public launch 5 months ago, SST has grown to over 2K stars on GitHub and has been downloaded over 60K times.

We would like to take this opportunity to thank SST community for being involved right from day one. We couldn't have built SST without you. Your continued feedback has pushed us to improve every part of the framework. It has also allowed us to truly understand the problems developers face while trying to adopt serverless. Also thanks to you, our Slack community has grown to nearly 500 members and is a welcoming space for anybody that's looking to use serverless.

As more and more developers discover and adopt SST's live local development environment; our team is committed to adding features to support their workflow. This commitment shows clearly in our rapid release history.

We are also incredibly proud of what we've built with the SST Guide, it is the most widely read resource for serverless. And Seed; the best way for teams to deploy and manage their serverless applications.

The new funding allows us to expand the team to better serve the community and ensure that you have everything you need to build serverless applications. So if you are excited about where we are heading and would like to help our cause, we would like you to join our team!


Www Src Content Docs Blog Sst Dev (`www/src/content/docs/blog/sst-dev.mdx`)


title: SST.dev author: jay template: splash lastUpdated: 2022-06-25 pagefind: false

We are now officially changing our name from Serverless Stack to SST. We are also moving our domain from serverless-stack.com to sst.dev.

We were previously called Serverless Stack and we were hosted at serverless-stack.com. Aside from being really long, it was also easy to confuse us with the Serverless Framework folks. We've been informally using SST but now with the new domain, we can make the change official!

You can call us SST and visit us at sst.dev!


Www Src Content Docs Blog Sst V1 (`www/src/content/docs/blog/sst-v1.mdx`)


template: splash title: SST v1 author: jay lastUpdated: 2022-05-16 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

SST has now officially hit v1. Join us at the SST 1.0 Conf as we celebrate its launch!

A huge thanks to everybody that helped test out the v1 beta versions!

Background

SST was released a little over a year ago. Since then we’ve added a web based dashboard and dozens of constructs!

While SST has been remarkably stable since its early releases, we learnt that there were some inconsistencies and confusing bits in the API. We also felt like we could do a better job of providing inline documentation.

Goals

SST v1 addresses this by:

  • Making it easy to connect stacks
  • Adding consistent interfaces for:
    • Underlying AWS resources
    • Underlying AWS resource names and ARNs
  • Reducing the need for CDK imports
  • Supporting inline TS Docs
  • Laying the foundation for full typesafety

You can read more about SST v1’s goals over on the docs.

Migrating

Migrating to v1 should be very straightforward. Most of the changes are reorganizing and renaming construct properties, attributes, and adding TS Docs. The underlying resources (ie. the CloudFormation template) are mostly not impacted. There should be zero downtime during the upgrade.

You can follow the steps in the migration guide to update.

SST v1

With the v1 release of SST, we’ve put together the key building blocks you need to create full-stack serverless applications. Let’s take a quick look at what SST v1 can do for you.

Easy to use Constructs

At the heart of SST are its easy to use constructs. These allow you to define the infrastructure that your application needs.

These constructs also come with great editor support. So you can both auto-complete on fields and read the docs inline.

Functional Stacks

In native CDK, stacks are defined as classes. This requires a great deal of boilerplate code and makes it confusing to reference resources across stacks. To simplify this, SST v1 introduces the concept of Functional Stacks.

SST Console

SST also comes with a web based dashboard to manage your apps.

Live Lambda Dev

When you run sst start, it’ll start up the Live Lambda Development environment. This allows you to set breakpoints and test your Lambda functions locally.

SST 1.0 Conf

To commemorate SST hitting v1 we are holding the SST 1.0 Conf. It’s a free virtual event filled with talks from the community including folks from Comcast, LEGO, Analog Devices, and Shell.

It's streaming live on May 17th at 9am PDT. We hope to see you there!

Looking ahead

It’s been an amazing year for SST. When I think about everything we’ve accomplished, I can’t help but wonder that it wouldn’t have happened without our community! A huge thanks to all of you for your support.

We have some really exciting updates in store for you as we step into this new phase of development for SST. We’ll be sharing more about our direction in the coming weeks and we are giving a sneak peek at the SST 1.0 Conf. So stay tuned!


Www Src Content Docs Blog Sst V2 Preview (`www/src/content/docs/blog/sst-v2-preview.mdx`)


title: SST v2 Preview template: splash author: jay lastUpdated: 2022-12-06 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

As the first episode of the Is serverless ready? series, we are showing off a preview of SST's upcoming v2 release.

You can check it out on YouTube here.

The video is timestamped and here's roughly what's going to be released as a part of SST v2:

  1. Monopackage β€” SST is now going to use a monopackage. Moving from @serverless-stack to simply sst.
  2. New config β€” There's a new config file. A .js file instead of the .json. Similar to what Vite has.
  3. New functions API β€” We've refactored the props in the SST Functions construct.
  4. Improved codegen β€” We've reworked how SST codegens your types.
  5. pnpm support β€” We've made SST compatible with pnpm. We've internally migrated to pnpm as well!
  6. New CLI UI β€” There's an all new CLI UI, that makes it easier to figure out what the CLI is doing.
  7. Faster CLI β€” We've refactored the entire CLI codebase and optimized for speed. It's a lot faster than before.
  8. Faster Live Lambda Dev β€” We've also adopted a new debug stack infrastructure, so Live Lambda Dev is now way faster than before!
  9. Building modern frontends β€” We are also going to be introducing new constructs for Astro and Solid.
  10. OpenNext β€” We also launched a new open source initiative called OpenNext to allow Next.js apps to work on AWS, exactly like they do on Vercel.

SST v2 is now in beta and we need your help testing it. So make sure to join us on Discord and take it for a spin.


Www Src Content Docs Blog Sst V2 (`www/src/content/docs/blog/sst-v2.mdx`)


title: SST v2 lastUpdated: 2023-02-27 template: splash author: jay cover: /social-cards/blog/sst-v2.png pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

SST v2 is ready. Here's what's new and how you can learn more about it.

What is SST v2

We completely rewrote SST to be simpler, faster, and easier to work with. Check out this video to get a quick sense of what SST v2 is all about.

Adam and Dax also did a whole podcast on SST v2 and the rewrite process.

We also did a launch livestream where we talked about what's new in v2, how to upgrade, and deep dive into the new codebase. We also answered some questions from the community.


Upgrade to v2

In general most of the changes are ergonomic and it should be a pretty simple upgrade process. Check out our docs for the upgrade guide.


Features

Here's a quick rundown of the major changes.

  • Monopackage

    The first major change is that we are moving to a new npm package β€” sst

    We are also moving to a monopackage architecture. So instead of installing @serverless-stack/cli or @serverless-stack/resources; you simply need to install sst.

    All the constructs can be imported from sst/constructs. And the Node client is under sst/node.

  • New CLI

    This new package also powers a brand new CLI. It's faster and has a cleaner UI to go with it.

    bash
    SST v2.0.38  ready!
    
    β†’ App:     my-sst-app
      Stage:   Jay
      Console: https://console.sst.dev/my-sst-app/Jay
    
    βœ“ Deployed:
      Database
      Api
      API: https://4f574d6lqc.execute-api.us-east-1.amazonaws.com
      Web
      SITE: https://localhost:3000
  • Faster Live Lambda

    We've reworked how Live Lambda works! It's a lot faster than before and it does not need to deploy any new infrastructure for it. So no more Debug Stack!

    You can read more about the changes here.

  • sst.config.ts

    We've also changed how you configure your SST apps. We've replaced the old sst.json with a sst.config.ts.

    typescript
    export default {
      config(_input) {
        return {
          name: "my-sst-app",
          region: "us-east-1",
        };
      },
      stacks(app) {
        app.stack(Database).stack(Api).stack(Web);
      },
    } satisfies SSTConfig;
  • New frontends

    With SST v2, we've upgraded support for all the modern frontends out there. Our NextjsSite construct supports Next.js 13, thanks to our new OpenNext project.

    We also added a AstroSite construct for Astro and the SolidStartSite construct for Solid.

  • pnpm support

    SST now natively supports pnpm! Our new monorepo templates are now built to work with pnpm as well.

  • And more...

    There's also a new cleaned up functions API, a better way to generate types, and more. For all the details check out this preview of SST v2 we did recently.


Seed also supports SST v2. So go ahead and upgrade your apps and join us on Discord if you have any questions!


Www Src Content Docs Blog Sst V3 (`www/src/content/docs/blog/sst-v3.mdx`)


template: splash title: SST v3 author: jay lastUpdated: 2024-08-20 pagefind: false

import config from '../../../../config.ts';
import { Tabs, TabItem } from '@astrojs/starlight/components';
import { YouTube } from '@astro-community/astro-embed-youtube';

Earlier this year we had announced that we were working on a new deployment engine for SST called Ion. After months of testing and a gradual rollout, Ion is now officially being released as SST v3.


Background

SST was built on AWS CDK and CloudFormation. But after nearly 3 years of working with CDK and CloudFormation we hit some of its underlying design flaws and were forced to look at alternatives. We talked about this at length in a previous blog post.

Late last year we started work on a new deployment engine that would use Pulumi and Terraform, as opposed to CDK and CloudFormation.

How We Got Here

This new deployment engine, code named Ion has now been around for roughly half a year. And for the past few months we've been recommending people start new projects with Ion instead.

The number of people using Ion is close to the number of people using v2.

Fast forward to today, and the number of people actively using Ion is fairly close to the number of people using SST v2. Ion is also being used in production by a sizeable number of teams.

However given the nature of the change, the plan has been to get to a place where we'd feel comfortable enough to recommend migrating to Ion.


Ion β†’ SST v3

After months of hard work, we are finally here. We are now releasing Ion as SST v3.

The simpler deployment model has also led to far fewer gotchas.

Aside from being a lot faster, v3 addressed most of the issues that we had found with the CDK and CFN setup. The simpler deployment model has also led to far fewer gotchas. And it has allowed us to expand past AWS and support the broader ecosystem.

Along with the v3 release, we're also making a couple of changes to our websites, how we do support, the SST Console, and more. In this post we'll go over all of these changes and the migration process.

New Docs

Starting with the first big change, ion.sst.dev is now just sst.dev. You might've already noticed the new design.

The docs, sst.dev/docs are also brand new. The API and reference docs are completely generated from code. This means that they're always up to date and you can view them inline in your code editor.


Major Changes

As expected, there are some major changes in v3.

  1. No CloudFormation

    This one is obvious but it means that there are no stacks and no resource limits. The deployment happens right from your local machine. And the state is stored locally and backed up to S3 in your AWS account.

  2. No CDK

    There are no CDK constructs. Instead there is an AWS provider from Pulumi that's built on Terraform. These are like your CDK L1 constructs. That said, the Pulumi/Terraform ecosystem is fairly complete. Aside from AWS, SST now supports 150+ other providers.

  3. sst.config.ts

    The structure of the SST config has changed a little.

    ```ts title="sst.config.ts" export default $config({ // Your app's config app(input) { return { name: "my-sst-app", home: "aws" }; }, // Your app's resources async run() { } }); ``` ```ts title="sst.config.ts" export default { // Your app's config config(_input) { return { name: "my-sst-app", region: "us-east-1" }; }, // Your app's resources stacks(app) { } } satisfies SSTConfig; ```
  4. sst dev

    The sst dev CLI now runs a multiplexer that deploys your app and runs your frontends locally. So now you don't have to run sst dev and then go start your frontend. You also don't need to wrap your frontend's dev script with sst bind.

And more. We have a section in our migration doc that goes over all the changes in detail.


Learn More

Given that v3 is such a big change, we've got a couple of resources that can help you learn more about it.

These are great for getting started with SST in general. But we also recommend checking them out even if you've been using v2.

  • Docs

    Start with the What is SST and Basics docs. These give you a good overview and lead you to other docs that expand on the various concepts.

  • Guide

    Our SST Guide has been updated to use v3. It teaches you how to build a full-stack app on AWS with SST.

  • Video

    To get a more in-depth look at a v3 app in production, we put together this video detailing how terminal was built with v3. It's comprehensive and you can view the repo on GitHub as well.


Migration

We've created a detailed migration guide on how to migrate your v2 apps to v3.

It's a lot simpler to import resources.

One of the biggest advantages with this new deployment engine is that it's a lot simpler to import resources. This makes migrating to v3 easier.

That said, there are some caveats:

  • If you were heavily reliant on L3 CDK Constructs in your SST apps, it's going to be hard to find direct replacements for those in the Pulumi/Terraform ecosystem.
  • There are something that aren't supported yet. But they are on our roadmap.
    • Some constructs; like Job, Auth, EventBus that we haven't added support for yet.
    • Non-Node runtimes for Lambda functions aren't supported yet either.
    • SST v3 does not work on Windows yet, you'll need to use WSL.

Support

We know that you are going to need some help migrating to v3. To prepare for this we've made some changes to how we do support.

We are now using GitHub Issues as our primary channel. We've been using it while working on Ion and we are using it to track both issues and feature requests.

We also streamlined our Discord channels. Instead of having separate channels for the different parts of SST, you can just post your questions in #general.


Console

The Console has also been updated to support v3. There are a couple of key things that the Console does a better job of with v3 apps.

  1. Resources:
    The Console shows you all the resources in your app, not just the SST components. This includes the outputs of each of the resources as well.
  2. Updates:
    You can also see all the updates that are made to your app along with the resources that've been changed. So you can see when a resource was added, removed, or modified.
  3. Autodeploy:
    You can now connect your GitHub repo to the Console and it'll auto-deploy your v3 apps when you git push to it. You can also customize this workflow through your sst.config.ts.

If you are using Seed to deploy your SST v2 apps, we recommend migrating over to the Console for v3. We are currently not planning to support v3 in Seed.


What About v2

The v3 migration is not simple and there are likely a few of you that won't be able to make the change. So we are planning on keeping v2 updated.

We'll accept PRs and release updates. But we won't add new constructs or introduce breaking changes.

The SST v2 docs have been moved to v2.sst.dev.


We know this is a big change and you might have some concerns about it. Feel free to join us on Discord and we'll be happy to address them.

If you are an enterprise you can <a href={mailto:${config.email}}>get in touch with us directly via email as well.


Www Src Content Docs Blog Tasks In V3 (`www/src/content/docs/blog/tasks-in-v3.mdx`)


template: splash title: Tasks in v3 author: jay lastUpdated: 2024-12-29 pagefind: false

import { Image } from "astro:assets";
import { YouTube } from "@astro-community/astro-embed-youtube";
import cliImage from "../../../assets/blog/task-cli.png";
import consoleImage from "../../../assets/blog/task-console.png";

We are adding Task, a new component, powered by AWS Fargate that allows you to run asynchronous tasks in your apps. Here's a video where we talk about this and async jobs in general.

Background

Most applications have a need to run some background tasks. Typically these take a long time to run so they are triggered asynchronously. Or they are invoked through a cron job. Unfortunately you can't run them in a Lambda function because they might take longer than 15 minutes.

And since these are triggered asynchronously, they can be harder to test locally. You can mock their invocation but it'd be much better to test them through the usual flow of your application.

To fix this, we are adding the new Task component.


`Task`

  1. It uses AWS Fargate that can run as long as you need and is cheaper than Lambda.
  2. Can be invoked directly from a cron job.
  3. Comes with a JS SDK, but can also be invoked with the AWS SDK.
  4. Has its own dev mode, so it can be invoked remotely but it'll run locally.

You can check out an example if you want a quick start.


Getting started

Tasks are built on AWS Fargate and are tied to an Amazon ECS cluster. And so Task is created as a part of the Cluster component.

Create a task

ts title="sst.config.ts"
const cluster = new sst.aws.Cluster("MyCluster", { vpc });
const task = new sst.aws.Task("MyTask", { cluster });

By default, this looks for a Dockerfile in the root directory. You can configure this.

ts title="sst.config.ts"
new sst.aws.Task("MyTask", {
  cluster,
  image: {
    context: "./app",
    dockerfile: "Dockerfile",
  },
});

Run the task

Once created, you can run the task through:

  1. Task SDK

    With the Task JS SDK, you can run your tasks, stop your tasks, and get the status of your tasks.

    You can call this from your functions, frontends, or container services. For example, you can link the task to a function.

    ts title="sst.config.ts" {3}
    new sst.aws.Function("MyFunction", {
      handler: "src/lambda.handler",
      link: [task],
    });

    Then from your function start the task.

    ts title="src/lambda.ts" {4}
    import { Resource } from "sst";
    import { task } from "sst/aws/task";
    
    const runRet = await task.run(Resource.MyTask);
    const taskArn = runRet.tasks[0].taskArn;

    Other languages

    The JS SDK is calling the AWS ECS SDK behind the scenes. So if you are using a different language, you can directly call the AWS SDK. Here's how to run a task.

  2. Cron jobs

    You can also connect your task to a Cron job.

    ts title="sst.config.ts" {2}
    new sst.aws.Cron("MyCronJob", {
      task,
      schedule: "rate(1 day)",
    });

    This works by connecting the task to the cron job through EventBridge.


Dev mode

You can test your tasks locally in sst dev in a similar way to how you test your functions Live.

Any tasks that are invoked remotely are proxied to your local machine that runs the dev.command you have. These also show up under the Tasks tab in the multiplexer sidebar.

ts title="sst.config.ts" {3}
new sst.aws.Task("MyTask", {
  dev: {
    command: "node src/tasks.js",
  },
});

If your Vpc has bastion enabled, then your tasks have access to resources in your VPC as well.


Console logs

The Console supports viewing logs for your tasks when they are in production.


Cost

You are only charged for the time it takes to run the task. With the default memory and vCPU it costs roughly $0.02 per hour.

When running in sst dev, you are charged for the time it takes to run the task locally as well.


Next steps

Learn more in our docs.

And check out these examples.


Comparison to v2

If you are coming from SST v2, there are a couple of differences between Task and Job.

  1. Task is based on AWS Fargate. Job used a combination of AWS CodeBuild and Lambda.
  2. Since Task is natively based on Fargate, you can use the AWS SDK to interact with it, even in runtimes the SST SDK doesn't support.
  3. In dev mode, Task uses Fargate only, whereas Job used Lambda.
  4. While CodeBuild is billed per minute, Fargate is a lot cheaper than CodeBuild. Roughly $0.02 per hour vs $0.3 per hour on X86 machines.

Www Src Content Docs Blog Testing In Sst (`www/src/content/docs/blog/testing-in-sst.mdx`)


template: splash title: Testing in SST author: jay image: /social-cards/blog/sst-testing.png lastUpdated: 2022-08-29 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

You can write tests for your SST apps using the new sst load-config CLI. It auto-loads secrets and config for your tests.

Meaning that it'll load the Config of your app, but for your tests.

So for example, this command loads your Config and runs your tests using Vitest.

bash
$ sst load-config -- vitest run

Also, make sure to check out our launch announcement for Config in case you missed it.

We updated the create sst GraphQL starter to include the updated npm test script.

js
"test": "sst load-config -- vitest run"

We also have a new chapter in our docs dedicated to testing. It includes how to write tests for the different parts of your app:

  • Tests for your domain code. Recall that we recommend Domain Driven Design.
  • Tests for your APIs, the endpoints handling requests.
  • Tests for your stacks, the code that creates your infrastructure.

Launch event

We hosted a launch livestream on YouTube where we talked about how to write tests for your SST apps.

The video is timestamped and here's roughly what we covered.

  1. Intro
  2. Setting up a new app
  3. Testing domain code
  4. Testing APIs
  5. Testing stacks
  6. Deep dive into sst load-config
  7. Q&A
    1. How to test asynchronous workflow?
    2. How to run tests in parallel?

Get started

To get started, create a new SST app with our GraphQL starter.

bash
$ npx create-sst@latest

Make sure to select the graphql and DynamoDB option.

Next, install the dependencies.

bash
$ cd my-sst-app
$ npm install

Let's deploy the app once. This'll create all the infrastructure for your app and the Config. Recall that we use AWS SSM to store our secrets and parameters.

bash
$ npm run deploy

And you can run your tests by running:

bash
npm test

Make sure to check out the sample test included with the starter to get a sense of how it all works!

To learn more check out our docs.



import { Image } from "astro:assets"

import updatesLight from '../../../assets/blog/sst-console-updates-light.png';
import updatesDark from '../../../assets/blog/sst-console-updates-dark.png';

Moving forward, every update made to an SST v3 app will get a unique URL; a permalink. This is printed out by the SST CLI.

bash title="sst deploy"
β†—  Permalink   https://sst.dev/u/318d3879

These permalinks redirect to a page in the Console. Check out a short video of this in action.

Here you can view:

  1. Full list of all the resources that were modified
  2. Changes in their inputs and outputs
  3. Any Docker or site builds logs
  4. CLI command that triggered the update
  5. Git commit, if it was an auto-deploy

You'll need to have your AWS account connected to the Console to view these details.


How it helps

The permalinks are useful for.

  1. Debugging deploys: The changes in the inputs of a resource let you see if the changes to your sst.config.ts have been applied correctly.
  2. Speeding up deploys: You can check which resources are taking long. Or why a certain resource is being updated in the first place.
  3. Sharing with your team: Say you run into an error while running sst dev locally; now you can send this link to a teammate. And they can view the full event log instead of asking you to send them the log file from your local machine.

How it works

In addition to the state updates, the SST CLI also uploads the event log from every update to the S3 state bucket in your AWS account. It also generates a globally unique id for the update.

If your AWS account is connected to the Console, it pulls this state and generates the details for the update permalink. This also means that the Console can only show you updates after your AWS account has been connected.

When you visit the permalink, the Console looks up the id of the update and redirects you to the right app in your workspace.


You can learn more about updates in the Console and how state works over on the docs.


Www Src Content Docs Blog Windows Support In Beta (`www/src/content/docs/blog/windows-support-in-beta.mdx`)


title: Windows support in beta template: splash author: jay lastUpdated: 2025-04-28 pagefind: false

Previously, SST v3 was only supported on Linux/macOS and Windows with WSL. With v3.14, native Windows support is now in beta.

The main difference between Windows and Linux/macOS is that sst dev does not support the tabbed terminal UI. So while, it does spawn multiple processes, it does show their output in separate tabs.

If you are on Windows, we recommend upgrading to v3.14 and giving it a try. If you run into any problems, feel free to open a GitHub issue.


Www Src Content Docs Blog Wrapping Up Sst 1 0 Conf (`www/src/content/docs/blog/wrapping-up-sst-1-0-conf.mdx`)


template: splash title: Wrapping up SST 1.0 Conf author: jay image: /social-cards/blog/sst-1-0-conf-replay.png lastUpdated: 2022-05-23 pagefind: false

import { YouTube } from '@astro-community/astro-embed-youtube';

Hopefully everybody enjoyed SST 1.0 Conf as much as we enjoyed putting it together. It allowed the community to come together and celebrate SST's v1 release.

It's been a little over a year since SST launched and it's incredible to see how far it has come in this short time. SST now has over 6500 stars on GitHub, 1900 developers in Slack, and nearly 80000 folks have subscribed to our newsletter. Thousands of teams from companies like Comcast, Shell, Analog Devices, and The LEGO Group are using SST.

Watch the recording

If you missed SST 1.0 Conf, don't worry you can watch the recording on YouTube.

All the talks are timestamped. So if you want to watch a specific talk, or rewatch one, you can do that right from YouTube.

A huge thanks to the speakers for taking the time from their busy schedules to make this event a success.

Looking ahead

Now that SST has hit v1, you might be wondering what we are doing next. We are going to be spending more time on the application code portion of your SST apps. To get an early preview of it, check out Dax's talk from the conference.

SST 1.0 Conf also allowed us as a team to do a couple of things that we don't usually do. It allowed us to share a side of us that's hard to do on Slack. So moving forward we'll be sharing more about how we work. We'll be doing more livestreams and creating screencasts. You can subscribe to our channel on YouTube.

Give us your feedback

We are already looking forward to the next SST conference. We've got a ton of ideas on what we'd like to do. But we'd appreciate if you took a second to give us some of your feedback on SST 1.0 Conf.

Once again, thanks to you all for attending SST 1.0 Conf and being a part of this amazing community!


Www Src Content Docs Docs All Providers (`www/src/content/docs/docs/all-providers.mdx`)


title: All Providers

import VideoAside from "../../../components/VideoAside.astro";

Aside from the built-in components, SST supports any of the 150+ Pulumi and Terraform providers.

Check out the full list in the Directory.


Add a provider

To add a provider to your app run.

bash
sst add <provider>

This command adds the provider to your config, installs the packages, and adds the namespace of the provider to your globals.

:::caution
You don't need to import the provider packages in your sst.config.ts.
:::

SST manages these packages internally and you do not need to import the package in your sst.config.ts.

For example, to add the Stripe provider.

bash
sst add stripe

Read more about providers.


Preloaded

SST comes preloaded with the following providers, so you don't need to add them.

These are used internally to power the built-in components.


Use a resource

Once added, you can use a resource from the provider in your sst.config.ts.

For example, use a Stripe resource in your config's run function.

ts title="sst.config.ts" {4-7}
export default $config({
  // ...
  async run() {
    new stripe.Product("MyStripeProduct", {
      name: "SST Paid Plan",
      description: "This is how SST makes money",
    });
  },
});

As mentioned above, since the AWS provider comes preloaded, you can use any AWS resource directly as well.

ts title="sst.config.ts"
new aws.apprunner.Service("MyService", {
  serviceName: "example",
  sourceConfiguration: {
    imageRepository: {
      imageConfiguration: {
        port: "8000"
      },
      imageIdentifier: "public.ecr.aws/aws-containers/hello-app-runner:latest",
      imageRepositoryType: "ECR_PUBLIC"
    }
  }
});

Directory

Below is the full list of providers that SST supports.

bash
sst add <provider>

Install any of the following using the package name as the provider. For example, sst add auth0.

If you want SST to support a Terraform provider or update a version, you can submit a PR to the sst/provider repo.


Provider Package
ACI @netascode/aci
ACME @pulumiverse/acme
Aiven aiven
Akamai akamai
Alibaba Cloud alicloud
Amazon EKS eks
Aquasec @pulumiverse/aquasec
Artifactory artifactory
Astra DB @pulumiverse/astra
Auth0 auth0
Auto Deploy auto-deploy
AWS API Gateway aws-apigateway
AWS aws
AWS Control Tower @lbrlabs/pulumi-awscontroltower
AWS IAM aws-iam
AWS Cloud Control aws-native
AWS QuickStart Aurora Postgres aws-quickstart-aurora-postgres
AWS QuickStart Redshift aws-quickstart-redshift
AWS QuickStart VPC aws-quickstart-vpc
AWS S3 Replicated Bucket aws-s3-replicated-bucket
AWS Static Website aws-static-website
AWSx awsx
AzAPI @ediri/azapi
Azure Active Directory azuread
Azure Classic azure
Azure Justrun pulumi-azure-justrun
Azure Native azure-native
Azure Quickstart ACR Geo Replication azure-quickstart-acr-geo-replication
Azure QuickStart ACR Geo Replication azure-quickstart-acr-geo-replication
Azure Static Website azure-static-website
AzureDevOps azuredevops
Buildkite @pulumiverse/buildkite
Checkly @checkly/pulumi
Cisco Catalyst SD-WAN sdwan
Cisco ISE ise
Civo civo
Cloud-Init cloudinit
CloudAMQP cloudamqp
Cloudflare cloudflare
CockroachDB @pulumiverse/cockroach
Command command
Confluent confluentcloud
Consul consul
Control Plane @pulumiverse/cpln
Databricks databricks
Datadog datadog
dbt Cloud dbtcloud
DigitalOcean digitalocean
DNSimple dnsimple
Docker docker
Docker Build docker-build
Doppler @pulumiverse/doppler
Dynatrace @pulumiverse/dynatrace
Elastic Cloud ec
Equinix @equinix-labs/pulumi-equinix
ESXi Native @pulumiverse/esxi-native
Event Store Cloud @eventstore/pulumi-eventstorecloud
Exoscale @pulumiverse/exoscale
F5 BIG-IP f5bigip
Fastly fastly
Flux @worawat/flux
Fortios @pulumiverse/fortios
FusionAuth pulumi-fusionauth
Gandi @pulumiverse/gandi
GCP Global CloudRun gcp-global-cloudrun
Genesis Cloud @genesiscloud/pulumi-genesiscloud
GitHub github
GitLab gitlab
Google Cloud Classic gcp
Google Cloud Native google-native
Google Cloud Static Website google-cloud-static-website
Grafana @pulumiverse/grafana
Harbor @pulumiverse/harbor
Harness harness
HashiCorp Vault vault
HCP @grapl/pulumi-hcp
Hetzner Cloud hcloud
Impart Security @impart-security/pulumi-impart
InfluxDB @komminarlabs/influxdb
Kafka kafka
Keycloak keycloak
Kong kong
Koyeb @koyeb/pulumi-koyeb
Kubernetes kubernetes
Kubernetes Cert Manager kubernetes-cert-manager
Kubernetes CoreDNS kubernetes-coredns
LaunchDarkly lauchdarkly
LBr Labs EKS @lbrlabs/pulumi-eks
libvirt libvirt
Linode linode
Mailgun mailgun
Matchbox @pulumiverse/matchbox
Miniflux aws-miniflux
MinIO minio
MongoDB Atlas mongodbatlas
MSSQL @pulumiverse/mssql
MySQL mysql
Neon neon
New Relic newrelic
NGINX Ingress Controller kubernetes-ingress-nginx
ngrok @pierskarsenbarg/ngrok
Nomad nomad
NS1 ns1
Nuage nuage
Nutanix @pierskarsenbarg/nutanix
Okta okta
OneLogin onelogin
OpenStack openstack
Opsgenie opsgenie
Oracle Cloud Infrastructure oci
OVHCloud @ovh-devrelteam/pulumi-ovh
PagerDuty pagerduty
Pinecone @pinecone-database/pulumi
PlanetScale planetscale
Port @port-labs/port
PostgreSQL postgresql
Prodvana @prodvana/pulumi-prodvana
Proxmox Virtual Environment @muhlba91/pulumi-proxmoxve
Pulumi Cloud pulumiservice
purrl @pulumiverse/purrl
Qovery @ediri/qovery
RabbitMQ rabbitmq
Rancher2 rancher2
Railway railway
random random
Redis Cloud @rediscloud/pulumi-rediscloud
Rootly @rootly/pulumi
Runpod @runpod-infra/pulumi
Scaleway @pulumiverse/scaleway
Sentry @pulumiverse/sentry
SignalFx signalfx
Slack slack
Snowflake snowflake
Splight @splightplatform/pulumi-splight
Splunk splunk
Spotinst spotinst
Statuscake @pulumiverse/statuscake
Strata Cloud Manager scm
Stripe stripe
Stripe Official stripe-official
StrongDM @pierskarsenbarg/sdm
Sumo Logic sumologic
Supabase supabase
Symbiosis @symbiosis-cloud/symbiosis-pulumi
Synced Folder synced-folder
Tailscale tailscale
Talos Linux @pulumiverse/talos
Time @pulumiverse/time
TLS tls
Twingate @twingate/pulumi-twingate
Unifi @pulumiverse/unifi
Upstash @upstash/pulumi
Venafi venafi
Vercel vercel
VMware vSphere vsphere
Volcengine @volcengine/pulumi
vSphere vsphere
Vultr @ediri/vultr
Wavefront wavefront
Yandex yandex
Zitadel @pulumiverse/zitadel
Zscaler Internet Access @bdzscaler/pulumi-zia
Zscaler Private Access @bdzscaler/pulumi-zpa

Any missing providers or typos? Feel free to Edit this page and submit a PR.


Www Src Content Docs Docs Aws Accounts (`www/src/content/docs/docs/aws-accounts.mdx`)


title: Set up AWS Accounts

Unsurprisingly there are multiple ways to set up AWS accounts. And unfortunately the default process misses a few things that'll likely make this a lot easier for your team.

:::tip
If you are using IAM users or have credential files, this guide is for you.
:::


The ideal setup is to have multiple AWS accounts grouped under a single AWS Organization. While your team authenticates through SSO to access the Console and the CLI.

While this sounds complicated, it's a one time process that you'll never have to think about again.

Let's get started.


Management account

The first step is to create a management account.

  1. Start by using a work email alias. For example [email protected]. This'll forward to your real email. It allows you to give other people access to it in the future.
  2. The account name should be your company name, for example acme.
  3. Enter your billing info and confirm your identity.
  4. Choose basic support. You can upgrade this later.

Once you're done you should be able to login and access the AWS Console.

These credentials are overly powerful. You should rarely ever need them again. Feel free to throw away the password after completing this guide. You can always do a password reset if it's needed.

:::tip
The Management account is what you'll use to manage the users in your organization.
:::

This account won't have anything deployed to it besides the IAM Identity Center which is how we'll manage the users in our organization.


AWS Organization

Next, we'll create an organization. This allows you to manage multiple AWS accounts together. We'll need this as we create separate accounts for dev and prod.

Search AWS Organization in the search bar to go to its dashboard and click Create an organization.

You'll see that the management account is already in the organization.


IAM Identity Center

Now let's enable IAM Identity Center.

  1. Search IAM Identity Center and go to its dashboard. Click Enable.

    :::note
    Make a note of the region you're in for the IAM Identity Center.
    :::

    This'll be created in one region and you cannot change it. However, it doesn't matter too much which one it is. You'll just need to navigate to that region when you are trying to find this again.

  2. Click Enable. This will give your organization a unique URL to login.

    :::note
    Make a note of the URL that IAM Identity Center gives you.
    :::

    This is auto-generated but you can click Customize to select a unique name. You'll want to bookmark this for later.


Root user

Now we'll create a root user in IAM Identity Center.

  1. Click Users on the left and then Add user to create a user for yourself. Make your username your work email, for example [email protected], and fill out the required fields.
  2. Skip adding the user to groups.
  3. Finish creating the user.

We've created the user. Now let's give it access to our management account.


User access

Go to the left panel and click AWS Accounts.

  1. Select your management account. It should be tagged as such. And click Assign users or groups.
  2. Select the Users tab, make sure your user is selected and hit Next.
  3. Now we'll need to create a new permission set. We need to do this once. Click Create permission set.
  4. In the new tab select Predefined permission set and AdministratorAccess. Click Next.
  5. Increase the session duration to 12 hours. This is the most convenient option. Click Next and then Create.
  6. Close the tab, return to the previous one and hit the refresh icon. Select AdministratorAccess and click Next and then Submit.

This might seem complicated but all we did was grant the user an AdministratorAccess role into the management account.

Now you're ready to log in to your user account.


Login

Check your email and you should have an invite.

  1. Accept the invite and create a new password. Be sure to save it in your password manager. This is important because this account has access to the management account.

    :::note
    If you already have an SSO provider, like Google you can allow your team to Login with Google. Let us know if you'd like us to document that as well.
    :::

  2. Sign in and you should see your organization with a list of accounts below it.

    You currently only have access to the management account we created above. So click it and you should see the AdministratorAccess role.

  3. Click Management Console to login to the AWS Console.

You're now done setting up the root user account!


Dev and prod accounts

As mentioned earlier, your management account isn't meant to deploy any resources. It's meant to manage users.

So a good initial setup is to create separate dev and production accounts. This helps create some isolation. The dev account will be shared between your team while the production account is just for production.

You can also create a staging account or an account per developer but we'll start simple.


Navigate back to AWS Organizations by searching for it.

  1. Click Add an AWS account.
  2. For the account name append -dev to whatever you called your management account. For example, acme-dev.
  3. For the email address choose a new email alias. If you're using Google for email, you can do [email protected] and it'll still go to your [email protected] email.
  4. Click Create AWS account.

Repeat this step and create the -production as well. So you should now have an acme-dev and an acme-production.

It'll take a few seconds to finish creating.


Assign users

Once it's done head over to IAM Identity Center to grant your user access to these accounts.

  1. Select the AWS Accounts tab on the left.
  2. Select your newly created acme-dev and acme-production accounts and click Assign users or groups.
  3. In the Users tab select your user and click Next.
  4. Select the AdministratorAccess permission set and click Next and Submit.

Now you can go back to your SSO URL. You should now see three different accounts and you'll be able to login to whichever one you want.

:::tip
You can find your SSO URL by clicking Dashboard on the left.
:::

You can create additional users and add them to these accounts using the steps above. You can reuse the role or create one with stricter permissions.

Next, let's configure the AWS CLI and SST to use this setup.


Configure AWS CLI

The great thing about this setup is that you no longer need to generate AWS IAM credentials for your local machine, you can just use SSO. This is both simpler and more secure.

:::tip
You can download the AWS CLI from the AWS docs.
:::

All you need is a single configuration file for the AWS CLI, SST, or any random scripts you want to run. And there will never be any long lived credentials stored on your machine.


  1. Add the following block to a ~/.aws/config file.

    bash title="~/.aws/config"
    [sso-session acme]
    sso_start_url = https://acme.awsapps.com/start
    sso_region = us-east-1

    Make sure to replace the sso_start_url with your SSO URL that you bookmarked. And set the region where you created IAM Identity Center as the sso_region.

  2. Add an entry for each environment, in this case dev and production.

    bash title="~/.aws/config"
    [profile acme-dev]
    sso_session = acme
    sso_account_id = <account-id>
    sso_role_name = AdministratorAccess
    region = us-east-1
    
    [profile acme-production]
    sso_session = acme
    sso_account_id = <account-id>
    sso_role_name = AdministratorAccess
    region = us-east-1

    You can find the account ID from your SSO login url. If you expand the account you will see it listed with a # sign.

    The region specified in the config is the default region that the CLI will use when one isn't specified.

    :::tip
    With this setup you won't need to save your AWS credentials locally.
    :::

    And the role name is the one we created above. If you created a different role, you'd need to change this.

  3. Now you can login by running.

    bash
    aws sso login --sso-session=acme

    This'll open your browser and prompt you to allow access. The sessions will last 12 hours, as we had configured previously.

    If you're using Windows with WSL, you can add a script to open the login browser of the host machine.

    View script

    sh title="login.sh"
    #!/bin/bash
    
    if grep -q WSL /proc/version; then
       export BROWSER=wslview
    fi
    
    aws sso login --sso-session=acme
  4. Optionally, for Node.js projects, it can be helpful to add this to a package.json script so your team can just run npm run sso to login.

    json title="package.json"
    "scripts": {
      "sso": "aws sso login --sso-session=acme"
    }
  5. Finally, test that everything is working with a simple CLI command that targets your dev account.

    bash
    aws sts get-caller-identity --profile=acme-dev

Next, let's configure SST to use these profiles.


Configure SST

In your sst.config.ts file check which stage you are deploying to and return the right profile.

ts title="sst.config.ts" {8}
export default $config({
  app(input) {
    return {
      name: "my-sst-app",
      home: "aws",
      providers: {
        aws: {
          profile: input.stage === "production" ? "acme-production" : "acme-dev"
        }
      }
    };
  },
  async run() {
    // Your resources
  }
});

This will use the acme-production profile just for production and use acme-dev for everything else.

:::note
The AWS_PROFILE environment variable will override the profile set in your sst.config.ts.
:::

If you've configured AWS credentials previously through the AWS_PROFILE environment variable or through a .env file, it will override the profile set in your sst.config.ts. So make sure to remove any references to AWS_PROFILE.

Now to deploy to your production account you just pass in the stage.

bash
sst deploy --stage production

And we are done!


To summarize, here what we've created:

  1. A management account to manage the users in our organization.
  2. A root user that can login to the management account.
  3. Dev and production accounts for our apps.
  4. Finally, given the root user access to both accounts.

You can extend these by adding more users, or adding additional accounts, or modifying the roles you grant.


Www Src Content Docs Docs Basics (`www/src/content/docs/docs/basics.mdx`)


title: Basics

import { Tabs, TabItem } from '@astrojs/starlight/components';
import VideoAside from "../../../components/VideoAside.astro";

The main difference between working on SST versus any other framework is that everything related to your app is all defined in code.

  1. SST automatically manages the resources in AWS (or any provider) defined in your app.
  2. You don't need to make any manual changes to them in your cloud provider's console.

This idea of automating everything can feel unfamiliar at first. So let's go through the basics and look at some core concepts.


Setup

Before you start working on your app, there are a couple of things we recommend setting up.

Starting with your code editor.


Editor

SST apps are configured through a file called sst.config.ts. It's a TypeScript file and it can work with your editor to type check and autocomplete your code. It can also show you inline help.

Most modern editors; VS Code and Neovim included, should do the above automatically. But you should start by making sure that your editor has been set up.


Credentials

SST apps are deployed to your infrastructure. So whether you are deploying to AWS, or Cloudflare, or any other cloud provider, make sure you have their credentials configured locally.

Learn more about how to configure your AWS credentials.


Console

SST also comes with a Console. It shows you all your apps, the resources in them, lets you configure git push to deploy, and also send you alerts for when there are any issues.

While it is optional, we recommend creating a free account and linking it to your AWS account. Learn more about the SST Console.


sst.config.ts

Now that you are ready to work on your app and your sst.config.ts, let's take a look at what it means to configure everything in code.


IaC

Infrastructure as Code or IaC is a process of automating the management of infrastructure through code. Rather than doing it manually through a console or user interface.

:::tip
You won't need to use the AWS Console to configure your SST app.
:::

Say your app has a Function and an S3 bucket, you would define that in your sst.config.ts.

ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket");

new sst.aws.Function("MyFunction", {
  handler: "index.handler"
});

You won't need to go to the Lambda and S3 parts of the AWS Console. SST will do the work for you.

In the above snippets, sst.aws.Function and sst.aws.Bucket are called Components. Learn more about Components.


Resources

The reason this works is because when SST deploys the above app, it'll convert it into a set of commands. These then call AWS with your credentials to create the underlying resources. So the above components get transformed into a list of low level resources in AWS.

:::tip
You are not directly responsible for the low level resources that SST creates.
:::

If you log in to your AWS Console you can see what gets created internally. While these might look a little intimidating, they are all managed by SST and you are not directly responsible for them.

SST will create, track, and remove all the low level resources defined in your app.


Exceptions

There are some exceptions to this. You might have resources that are not defined in your SST config. These could include the following resources:

  1. Previously created

    You might've previously created some resources by hand that you would like to use in your new SST app. You can import these resources into your app. Moving forward, SST will manage them for you. Learn more about importing resources.

  2. Externally managed

    You might have resources that are managed by a different team. In this case, you don't want SST to manage them. You simply want to reference them in your app. Learn more about referencing resources.

  3. Shared across stages

    If you are creating preview environments, you might not want to make copies of certain resources, like your database. You might want to share these across stages. Learn more about sharing across stages.


Linking

Let's say you wanted your function from the above example to upload a file to the S3 bucket, you'd need to hardcode the name of the bucket in your API.

SST avoids this by allowing you to link resources together.

ts title="sst.config.ts" {3}
new sst.aws.Function("MyFunction", {
  handler: "index.handler",
  link: [bucket]
});

Now in your function you can access the bucket using SST's SDK.

ts title="index.ts" "resource.mybucket.name"
import { Resource } from "sst";

console.log(Resource.MyBucket.name);

There's a difference between the two snippets above. One is your infrastructure code and the other is your runtime code. One is run while creating your app, while the other runs when your users use your app.

:::tip
You can access your infrastructure in your runtime using the SST SDK.
:::

The link allows you to access your infrastructure in your runtime code. Learn more about resource linking.


State

When you make a change to your sst.config.ts, like we did above. SST only deploys the changes.

diff lang="ts" title="sst.config.ts"
new sst.aws.Function("MyFunction", {
  handler: "index.handler",
+  link: [bucket]
});

It does this by maintaining a state of your app. The state is a tree of all the resources in your app and all their properties.

The state is stored in a file locally and backed up to a bucket in your AWS (or Cloudflare) account.

:::tip
You can view the state of your app and its history in the SST Console.
:::

A word of caution, if for some reason you delete your state locally and in your provider, SST won't be able to manage the resources anymore. To SST this app won't exist anymore.

:::danger
Do not delete the bucket that stores your app's state.
:::

To fix this, you'll have to manually re-import all those resources back into your app. Learn more about how state works.


Out of sync

We mentioned above that you are not responsible for the low level resources that SST creates. But this isn't just a point of convenience; it's something you should not do.

:::caution
Do not manually make changes to the low level resources that SST creates.
:::

The reason for this is that, SST only applies the diffs when your sst.config.ts changes. So if you manually change the resources, it'll be out of sync with your state.

You can fix some of this by running sst refresh but in general you should avoid doing this.


App

So now that we know how IaC works, a lot of the workflow and concepts will begin to make sense. Starting with the key parts of an app.


Name

Every app has a name. The name is used as a namespace. It allows SST to deploy multiple apps to the same cloud provider account, while isolating the resources in an app.

If you change the name of your app in your sst.config.ts, SST will create a completely new set of resources for it. It does not rename the resources.

:::caution
To rename an app, you'll need to remove the resources from the old one and deploy to the new one.
:::

So if you:

  1. Create an app with the name my-sst-app in your sst.config.ts and deploy it.
  2. Rename the app in your sst.config.ts to my-new-sst-app and deploy again.

You will now have two apps in your AWS account called my-sst-app and my-new-sst-app.

If you want to rename your app, you'll need to remove the old app first and then deploy a new one with the new name.


Stage

An app can have multiple stages. A stage is like an environment, it's a separate version of your app. For example, you might have a dev stage, a production stage, or a personal stage.

It's useful to have multiple versions of your app because it lets you make changes and test in one version while your users continue to use the other.

You create a new stage by deploying to it with the --stage <name> CLI option. The stage name is used as a namespace to create a new version of your app. It's similar to how the app name is used as a namespace.

:::caution
To rename a stage, you'll need to remove the resources from the old one and deploy to the new one.
:::

Similar to app names, stages cannot be renamed. So if you wanted to rename a development stage to dev; you'll need to first remove development and then deploy dev.


Personal stages

By default, if no stage is passed in, SST creates a stage using the username in your computer. This is called a personal stage. Personal stages are typically used in dev mode and every developer on your team should use their own personal stage.

We'll look at this in detail below.


Region

Most resources that are created in AWS (and many other providers) belong to a specific region. So when you deploy your app, it's deployed to a specific region.

:::caution
To switch regions, you'll need to remove the resources from one region and deploy to the new one.
:::

For AWS, the region comes from your AWS credentials but it can be specified in the sst.config.ts.

ts title="sst.config.ts" {5-7}
export default $config({
  app(input) {
    return {
      name: "my-sst-app",
      providers: {
        aws: { region: "us-west-2" }
      }
    };
  }
});

Similar to the app and stage, if you want to switch regions; you'll need to remove your app in the old region and deploy it to the new one.


Commands

Now with the above background let's look at the workflow of building an SST app.

Let's say you've created an app by running.

bash
sst init

Dev

To start with, you'll run your app in dev.

bash
sst dev

This deploys your app to your personal stage in dev mode. It brings up a multiplexer that deploys your app, runs your functions, creates a tunnel, and starts your frontend and container services.

It deploys your app a little differently and is optimized for local development.

  1. It runs the functions in your app Live by deploying a stub version. These proxy any requests to your local machine.
  2. It does not deploy your frontends or container services. Instead, it starts them locally.
  3. It also creates a tunnel that allows them to connect to any resources that are deployed in a VPC.

:::note
Only use sst dev in your personal stage.
:::

For this reason we recommend only using your personal stage for local development. And instead deploying to a separate stage when you want to share your app with your users.

Learn more about sst dev.


Deploy

Once you are ready to go to production you can run.

bash
sst deploy --stage production

You can use any stage name for production here.


Remove

If you want to remove your app and all the resources in it, you can run.

bash
sst remove --stage <name>

You want to be careful while running this command because it permanently removes all the resources from your AWS (or cloud provider) account.

:::caution
Be careful while running sst remove since it permanently removes all your resources.
:::

To prevent accidental removal, our template sst.config.ts comes with the following.

ts title="sst.config.ts"
removal: input?.stage === "production" ? "retain" : "remove",

This is telling SST that if the stage is called production then on remove, retain critical resources like buckets and databases. This should avoid any accidental data loss.

Learn more about removal policies.


With a team

This workflow really shines when working with a team. Let's look at what it looks like with a basic git workflow.

  1. Every developer on the team uses sst dev to work in their own isolated personal stage.
  2. You commit your changes to a branch called dev.
  3. Any changes to the dev branch are auto-deployed using sst deploy --stage dev.
  4. Your team tests changes made to the dev stage of your app.
  5. If they look good, dev is merged into a branch called production.
  6. And any changes to the production branch are auto-deployed to the production stage with sst deploy --stage production.

In this setup, you have a separate stage per developer, a dev stage for testing, and a production stage.


Autodeploy

To have a branch automatically deploy to a stage when commits are pushed to it, you need to configure GitHub Actions.

Or you can connect your repo to the SST Console and it'll auto-deploy your app for you. Learn more about Autodeploy.


PR environments

You can also set it up to create preview environments.

So when a pull request (say PR#12) is created, you auto-deploy a new stage using sst deploy --stage pr-12. And once the PR is merged, the preview environment or stage gets removed using sst remove --stage pr-12.

Just like above, you can configure this using GitHub Actions or let the SST Console do it for you.


And there you have it. You are now ready to build apps the SST way.


Www Src Content Docs Docs Changelog (`www/src/content/docs/docs/changelog.mdx`)


title: Changelog

import Changelog from "../../../components/Changelog.astro";

Release notes for SST. For older versions or the full commit history, see the GitHub releases.


Www Src Content Docs Docs Cloudflare (`www/src/content/docs/docs/cloudflare.mdx`)


title: Cloudflare

Cloudflare lets you deploy apps with Workers and connect services like D1, R2, and DNS. This guide covers how to set it up with SST.


Install

Add the Cloudflare provider to your SST app. Learn more about providers.

bash
sst add cloudflare

This adds the provider to your sst.config.ts.

ts title="sst.config.ts" {3}
{
  providers: {
    cloudflare: "5.37.1",
  },
}

If Cloudflare should store your app state, set home to "cloudflare". This is useful in setups where you plan to use Cloudflare as you main cloud provider.


Credentials

You can create an account token in the Cloudflare dashboard under Manage Account > API Tokens.

Start with the Edit Cloudflare Workers template and add these permissions:

  • Account - D1 - Edit
  • Zone - DNS - Edit

:::tip
If your app uses other Cloudflare products, add the permissions those features need.
:::

Give the token access to the account your application will be deploying to. If you are using Cloudflare DNS with SST, include the zones that SST should update.

Set CLOUDFLARE_DEFAULT_ACCOUNT_ID to the Cloudflare account ID that SST should use. If you leave it unset, SST falls back to the first account that Cloudflare returns for that token.

Then set these variables in your shell, .env, or CI environment before you deploy:

bash
export CLOUDFLARE_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
export CLOUDFLARE_DEFAULT_ACCOUNT_ID=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa

Deploying into Multiple Cloudflare Accounts

You can deploy cloudflare components into multiple Cloudflare accounts by specifying the accountId argument and providing a different Cloudflare Provider.

ts
// define a provider with another api token for a different account
const provider = new cloudflare.Provider("AnotherProvider", { apiToken: "bbbbbbbb_bbbbbbbbbbbb_bbbbbbbb" });

// specify resource in another account
const sstWorker = new sst.cloudflare.Worker(
  "MyWorker",
  { handler: "index.ts", accountId: "bbbbbbbb-bbbbbbbbbbbb-bbbbbbbb" },
  { provider },
);

:::caution
Do not link any resources which are deployed into different Cloudflare accounts.
:::


Components

SST includes Cloudflare components for Workers, storage, queues, cron jobs, AI bindings, and more.

Worker

Create a Cloudflare Worker and enable a URL so it can handle HTTP requests.

ts title="sst.config.ts"
const worker = new sst.cloudflare.Worker("MyWorker", {
  handler: "index.ts",
  url: true,
});

return {
  url: worker.url,
};

Use the Worker component to build APIs and edge handlers on Cloudflare.

Storage

Create Cloudflare storage resources and link them to your Worker. For example, here's a D1 database.

ts title="sst.config.ts"
const db = new sst.cloudflare.D1("MyDatabase");

new sst.cloudflare.Worker("MyWorker", {
  handler: "index.ts",
  link: [db],
  url: true,
});

Then access it in your handler through Resource.

ts title="index.ts"
import { Resource } from "sst";

export default {
  async fetch() {
    const row = await Resource.MyDatabase.prepare(
      "SELECT id FROM todo ORDER BY id DESC LIMIT 1",
    ).first();

    return Response.json(row);
  },
};

The same pattern works with Bucket for R2 and Kv for KV namespaces.

Queue

Use Queue for async work.

ts title="sst.config.ts"
const queue = new sst.cloudflare.Queue("MyQueue");

queue.subscribe("consumer.ts");

const producer = new sst.cloudflare.Worker("Producer", {
  handler: "producer.ts",
  link: [queue],
  url: true,
});

For scheduled work, use Cron to run a worker on a cron expression.

More components

Browse the component docs for Worker, Astro, Bucket, D1, Kv, Queue, Cron, Ai, Workflow, and RateLimit.

If you are using Cloudflare DNS with SST, use sst.cloudflare.dns with custom domains.


Cloudflare Vite plugin

Cloudflare SSR components like Astro or TanStack Start need the Cloudflare Vite plugin to work correctly.

:::caution
Do not include any Wrangler configuration files (wrangler.toml, wrangler.json) in your project. SST manages these for you and will generate them as needed.
:::

You need to configure the Vite plugin to use the SST-managed Wrangler config.

ts title="vite.config.ts"
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";

export default defineConfig({
  plugins: [
    cloudflare({
      configPath: process.env.SST_WRANGLER_CONFIG,
    }),
  ],
});

This environment variable is set by SST and ensures the plugin uses the generated Wrangler configuration.

:::tip
There is an open PR on the Cloudflare Workers SDK that will add automatic detection of the SST-managed Wrangler config. Once merged, you won't need to explicitly set configPath.
:::


Examples

Check out the full examples:


Www Src Content Docs Docs Components (`www/src/content/docs/docs/components.mdx`)


title: Components

import VideoAside from "../../../components/VideoAside.astro";

Every SST app is made up of components. These are logical units that represent features in your app; like your frontends, APIs, databases, or queues.

There are two types of components in SST:

  1. Built-in components β€” High level components built by the SST team
  2. Provider components β€” Low level components from the providers

Let's look at them below.


Background

Most providers like AWS are made up of low level resources. And it takes quite a number of these to put together something like a frontend or an API. For example, it takes around 70 low level AWS resources to create a Next.js app on AWS.

As a result, Infrastructure as Code has been traditionally only been used by DevOps or Platform engineers.

To fix this, SST has components that can help you with the most common features in your app.


Built-in

The built-in components in SST, the ones you see in the sidebar, are designed to make it really easy to create the various parts of your app.

For example, you don't need to know a lot of AWS details to deploy your Next.js frontend:

ts title="sst.config.ts"
new sst.aws.Nextjs("MyWeb");

And because this is all in code, it's straightforward to configure this further.

ts title="sst.config.ts"
new sst.aws.Nextjs("MyWeb", {
  domain: "my-app.com",
  path: "packages/web",
  imageOptimization: {
    memory: "512 MB"
  },
  buildCommand: "npm run build"
});

You can even take this a step further and completely transform how the low level resources are created. We'll look at this below.

:::tip
Aside from the built-in SST components, all the Pulumi/Terraform providers are supported as well.
:::

Currently SST has built-in components for two cloud providers.


AWS

The AWS built-in components are designed to make it easy to work with AWS.

:::tip
SST's built-in components make it easy to build apps with AWS.
:::

These components are namespaced under sst.aws.* and listed under AWS in the sidebar. Internally they use Pulumi's AWS provider.


Cloudflare

These components are namespaced under sst.cloudflare.* and listed under Cloudflare in the sidebar. Internally they use Pulumi's Cloudflare provider.


Constructor

To add a component to your app, you create an instance of it by passing in a couple of args. For example, here's the signature of the Function component.

ts
new sst.aws.Function(name: string, args: FunctionArgs, opts?: pulumi.ComponentResourceOptions)

Each component takes the following:

  • name: The name of the component. This needs to be unique across your entire app.
  • args: An object of properties that allows you to configure the component.
  • opts?: An optional object of properties that allows you to configure this component in Pulumi.

Here's an example of creating a Function component:

ts title="sst.config.ts"
const function = new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler"
});

Name

There are two guidelines to follow when naming your components:

  1. The names of SST's built-in components and components extended with Linkable.wrap need to be global across your entire app.

    This allows Resource Linking to look these resources up at runtime.

  2. Optionally, use PascalCase for the component name.

    For example, you might name your bucket, MyBucket and use Resource Linking to look it up with Resource.MyBucket.

    However this is purely cosmetic. You can use kebab case. So my-bucket, and look it up using Resource['my-bucket'].


Args

Each component takes a set of args that allow you to configure it. These args are specific to each component. For example, the Function component takes FunctionArgs.

Most of these args are optional, meaning that most components need very little configuration to get started. Typically, the most common configuration options are lifted to the top-level. To further configure the component, you'll need to use the transform prop.

Args usually take primitive types. However, they also take a special version of a primitive type. It'll look something like Input<string>. We'll look at this in detail below.


Transform

Most components take a transform prop as a part of their constructor or methods. It's an object that takes callbacks that allow you to transform how that component's infrastructure is created.

:::tip
You can completely configure a component using the transform prop.
:::

For example, here's what the transform prop looks like for the Function component:

  • function: A callback to transform the underlying Lambda function
  • logGroup: A callback to transform the Lambda's LogGroup resource
  • role: A callback to transform the role that the Lambda function assumes

The type for these callbacks is similar. Here's what the role callback looks like:

ts
RoleArgs | (args: RoleArgs, opts: pulumi.ComponentResourceOptions, name: string) => void

This takes either:

  • A RoleArgs object. For example:

    ts
    {
      transform: {
        role: {
          name: "MyRole"
        }
      }
    }

    This is merged with the original RoleArgs that were going to be passed to the component.

  • A function that takes RoleArgs. Here's the function signature:

    ts
    (args: RoleArgs, opts: pulumi.ComponentResourceOptions, name: string) => void

    Where args, opts, and name are the arguments for the Role constructor passed to Pulumi.

    So you can pass in a callback that takes the current RoleArgs and mutates it.

    ts
    {
      transform: {
        role: (args, opts) => {
          args.name = `${args.name}-MyRole`;
          opts.retainOnDelete = true;
        }
      }
    }

`$transform`

Similar to the component transform, we have the global $transform. This allows you to transform how a component of a given type is created.

:::tip
Set default props across all your components with $transform.
:::

For example, set a default runtime for your functions.

ts title="sst.config.ts"
$transform(sst.aws.Function, (args, opts) => {
  // Set the default if it's not set by the component
  args.runtime ??= "nodejs18.x";
});

This sets the runtime for any Function component that'll be created after this call.

The reason we do the check for args.runtime is to allow components to override the default. We do this by only setting the default if the component isn't specifying its own runtime.

ts title="sst.config.ts"
new sst.aws.Function("MyFunctionA", {
  handler: "src/lambdaA.handler"
});

new sst.aws.Function("MyFunctionB", {
  handler: "src/lambdaB.handler",
  runtime: "nodejs20.x"
});

So given the above transform, MyFunctionA will have a runtime of nodejs18.x and MyFunctionB will have a runtime of nodejs20.x.

:::note
The $transform is only applied to components that are defined after it.
:::

The args and opts in the $transform callback are what you'd pass to the Function component. Recall the signature of the Function component:

ts title="sst.config.ts"
new sst.aws.Function(name: string, args: FunctionArgs, opts?: pulumi.ComponentResourceOptions)

Read more about the global $transform.


Properties

An instance of a component exposes a set of properties. For example, the Function component exposes the following properties β€” arn, name, url, and nodes.

ts
const functionArn = function.arn;

These can be used to output info about your app or can be used as args for other components.

These are typically primitive types. However, they can also be a special version of a primitive type. It'll look something like Output<string>. We'll look at this in detail below.


Some of these properties are also made available via resource linking. This allows you to access them in your functions and frontends in a typesafe way.

For example, a Function exposes its name through its links.


Nodes

The nodes property that a component exposes gives you access to the underlying infrastructure. This is an object that contains references to the underlying Pulumi components that are created.

:::tip
The nodes that are made available reflect the ones that can be configured using the transform prop.
:::

For example, the Function component exposes the following nodes β€” function, logGroup, and role.


Outputs

The properties of a component are typically of a special type that looks something like, Output.

These are values that are not available yet and will be resolved as the deploy progresses. However, these outputs can be used as args in other components.

This makes it so that parts of your app are not blocked and all your resources are deployed as concurrently as possible.

For example, let's create a function with an url.

ts title="sst.config.ts"
const myFunction = new sst.aws.Function("MyFunction", {
  url: true,
  handler: "src/lambda.handler"
});

Here, myFunction.url is of type Output<string>. We want to use this function url as a route in our router.

ts {3} title="sst.config.ts"
new sst.aws.Router("MyRouter", {
  routes: {
    "/api": myFunction.url
  }
});

The route arg takes Input<string>, which means it can take a string or an output. This creates a dependency internally. So the router will be deployed after the function has been. However, other components that are not dependent on this function can be deployed concurrently.

You can read more about Input and Output types on the Pulumi docs.


Apply

Since outputs are values that are yet to be resolved, you cannot use them in regular operations. You'll need to resolve them first.

For example, let's take the function url from above. We cannot do the following.

ts title="sst.config.ts"
const newUrl = myFunction.url + "/foo";

This is because the value of the output is not known at the time of this operation. We'll need to resolve it.

The easiest way to work with an output is using .apply. It'll allow you to apply an operation on the output and return a new output.

ts title="sst.config.ts"
const newUrl = myFunction.url.apply((value) => value + "/foo");

In this case, newUrl is also an Output<string>.


Helpers

To make it a little easier to work with outputs, we have the following global helper functions.


`$concat`

This lets you do.

ts title="sst.config.ts"
const newUrl = $concat(myFunction.url, "/foo");

Instead of the apply.

ts title="sst.config.ts"
const newUrl = myFunction.url.apply((value) => value + "/foo");

Read more about $concat.


`$interpolate`

This lets you do.

ts title="sst.config.ts"
const newUrl = $interpolate`${myFunction.url}/foo`;

Instead of the apply.

ts title="sst.config.ts"
const newUrl = myFunction.url.apply((value) => value + "/foo");

Read more about $interpolate.


`$jsonParse`

This is for outputs that are JSON strings. So instead of doing this.

ts title="sst.config.ts"
const policy = policyStr.apply((policy) =>
  JSON.parse(policy)
);

You can.

ts title="sst.config.ts"
const policy = $jsonParse(policyStr);

Read more about $jsonParse.


`$jsonStringify`

Similarly, for outputs that are JSON objects. Instead of doing a stringify after an apply.

ts title="sst.config.ts"
const policy = policyObj.apply((policy) =>
  JSON.stringify(policy)
);

You can.

ts title="sst.config.ts"
const policy = $jsonStringify(policyObj);

Read more about $jsonStringify.


`$resolve`

And finally when you are working with a list of outputs and you want to resolve them all together.

ts title="sst.config.ts"
$resolve([bucket.name, worker.url]).apply(([bucketName, workerUrl]) => {
  console.log(`Bucket: ${bucketName}`);
  console.log(`Worker: ${workerUrl}`);
})

Read more about $resolve.


Versioning

SST components evolve over time, sometimes introducing breaking changes. To maintain backwards compatibility, we implement a component versioning scheme.

For example, we released a new version the Vpc that does not create a NAT Gateway by default. To roll this out the previous version of the Vpc component was renamed to Vpc.v1.

Now if you were using the original Vpc component, update SST, and deploy; you'll get an error during the deploy saying that there's a new version of this component.

This allows you to decide what you want to do with this component.


Continue with the old version

If you prefer to continue using the older version of a component, you can rename it.

diff title="sst.config.ts" lang="ts"
- const vpc = new sst.aws.Vpc("MyVpc");
+ const vpc = new sst.aws.Vpc.v1("MyVpc");

Now if you deploy again, SST knows that you want to stick with the old version and it won't error.


Update to the latest version

Instead, if you wanted to update to the latest version, you'll have to rename the component.

diff title="sst.config.ts" lang="ts"
- const vpc = new sst.aws.Vpc("MyVpc");
+ const vpc = new sst.aws.Vpc("MyNewVpc");

Now if you redeploy, it'll remove the previously created component and recreate it with the new name and the latest version.

This is because from SST's perspective it looks like the MyVpc component was removed and a new component called MyNewVpc was added.

:::caution
Removing and recreating components may cause temporary downtime in your app.
:::

Since these are being recreated you've to be aware that there might be a period of time when that resource is not around. This might cause some downtime, depending on the resource.


Www Src Content Docs Docs Configure A Router (`www/src/content/docs/docs/configure-a-router.mdx`)


title: Configure a Router

import { Image } from "astro:assets"
import { Tabs, TabItem } from '@astrojs/starlight/components';

import DevDiagram from "../../../assets/docs/router/dev-architecture.svg";
import ProdDiagram from "../../../assets/docs/router/prod-architecture.svg";

You can set custom domains on components like your frontends, APIs, or services. Each of these create their own CloudFront distribution. But as your app grows you might:

  1. Have multiple frontends, like a landing page, or a docs site, etc.
  2. Want to serve resources from different paths of the same domain; like /docs, or /api.
  3. Want to set up preview environments on subdomains.

Also since CloudFront distributions can take 15-20 minutes to deploy, creating new distributions for each of the components, and for each stage, can really impact how long it takes to deploy your app.

:::tip
The Router lets you create and share a single CloudFront distribution for your entire app.
:::

The ideal setup here is to create a single CloudFront distribution for your entire app and share that across components and across stages.

Let's look at how to do this with the Router component.


A sample app

To demo this, let's say you have the following components in your app.

ts title="sst.config.ts"
// Frontend
const web = new sst.aws.Nextjs("MyWeb", {
  path: "packages/web"
});

// API
const api = new sst.aws.Function("MyApi", {
  url: true,
  handler: "packages/functions/api.handler"
});

// Docs
const docs = new sst.aws.Astro("MyDocs", {
  path: "packages/docs"
});

This has a frontend, a docs site, and an API. In production we'd like to have:

  • example.com serve MyWeb
  • example.com/api serve MyApi
  • docs.example.com serve MyDocs

We'll create a Router for production.

In our dev stage we'd like to have:

  • dev.example.com serve MyWeb
  • dev.example.com/api serve MyApi
  • docs.dev.example.com serve MyDocs

For our PR stages or preview environments we'd like to have:

  • pr-123.dev.example.com serve MyWeb
  • pr-123.dev.example.com/api serve MyApi
  • docs-pr-123.dev.example.com serve MyDocs

We'll create a separate Router for the dev stage and share it across all the PR stages.

We are doing docs-pr-123.dev. instead of docs.pr-123.dev. because of a limitation with custom domains in CloudFront that we'll look at below.

Let's set this up.


Add a router

Instead of adding custom domains to each component, let's add a Router to our app with the domain we are going to use in production.

ts title="sst.config.ts"
const router = new sst.aws.Router("MyRouter", {
  domain: {
    name: "example.com",
    aliases: ["*.example.com"]
  }
});

The *.example.com alias is because we want to route to the docs. subdomain.

And use that in our components.

diff lang="ts" title="sst.config.ts"
// Frontend
const web = new sst.aws.Nextjs("MyWeb", {
  path: "packages/web",
+  router: {
+    instance: router
+  }
});

// API
const api = new sst.aws.Function("MyApi", {
  handler: "packages/functions/api.handler",
+  url: {
+    router: {
+      instance: router,
+      path: "/api"
+    }
+  }
});

// Docs
const docs = new sst.aws.Astro("MyDocs", {
  path: "packages/docs",
+  router: {
+    instance: router,
+    domain: "docs.example.com"
+  }
});

Next, let's configure the dev stage.


Stage based domains

Since we also want to configure domains for our dev stage, let's add a function that returns the domain we want, based on the stage.

ts title="sst.config.ts"
const domain = $app.stage === "production"
  ? "example.com"
  : $app.stage === "dev"
    ? "dev.example.com"
    : undefined;

Now when we deploy the dev stage, we'll create a new Router with our dev domain.

diff lang="ts" title="sst.config.ts"
const router = new sst.aws.Router("MyRouter", {
  domain: {
-   name: "example.com",
-   aliases: ["*.example.com"]
+   name: domain,
+   aliases: [`*.${domain}`]
  }
});

And update the MyDocs component to use this.

diff lang="ts" title="sst.config.ts"
// Docs
const docs = new sst.aws.Astro("MyDocs", {
  path: "packages/docs",
  router: {
    instance: router,
-    domain: "docs.example.com"
+    domain: `docs.${domain}`
  }
});

Preview environments

Currently, we create a new CloudFront distribution for dev and production. But we want to share the same distribution from dev in our PR stages.


Share the router

To do that, let's modify how we create the Router.

diff lang="ts" title="sst.config.ts"
- const router = new sst.aws.Router("MyRouter", {
+ const router = isPermanentStage
    ? new sst.aws.Router("MyRouter", {
       domain: {
         name: domain,
         aliases: [`*.${domain}`]
       }
     })
+   : sst.aws.Router.get("MyRouter", "A2WQRGCYGTFB7Z");

The A2WQRGCYGTFB7Z is the ID of the Router distribution created in the dev stage. You can look this up in the SST Console or output it when you deploy your dev stage.

ts title="sst.config.ts"
return {
  router: router.distributionID
};

We are also defining isPermanentStage. This is set to true if the stage is dev or production.

ts title="sst.config.ts"
const isPermanentStage = ["production", "dev"].includes($app.stage);

Let's also update our domain helper.

diff lang="ts" title="sst.config.ts"
const domain = $app.stage === "production"
  ? "example.com"
  : $app.stage === "dev"
    ? "dev.example.com"
-    : undefined;
+    : `${$app.stage}.dev.example.com`;

Since the domain alias for the dev stage is set to *.dev.example.com, it can match pr-123.dev.example.com. But not docs.pr-123.dev.example.com. This is a limitation of CloudFront.


Nested subdomains

So we'll be using docs-pr-123.dev.example.com instead.

:::note
Nested wildcards domain patterns are not supported.
:::

To do this, let's add a helper function.

ts title="sst.config.ts"
function subdomain(name: string) {
  if (isPermanentStage) return `${name}.${domain}`;
  return `${name}-${domain}`;
}

This will add the - for our PR stages. Let's update our MyDocs component to use this.

diff lang="ts" title="sst.config.ts"
// Docs
const docs = new sst.aws.Astro("MyDocs", {
  path: "packages/docs",
  router: {
    instance: router,
-    domain: `docs.${domain}`
+    domain: subdomain("docs")
  }
});

Wrapping up

And that's it! We've now configured our router to serve our entire app.

Here's what the final config looks like.

ts title="sst.config.ts"
const isPermanentStage = ["production", "dev"].includes($app.stage);

const domain = $app.stage === "production"
  ? "example.com"
  : $app.stage === "dev"
    ? "dev.example.com"
    : `${$app.stage}.dev.example.com`;

function subdomain(name: string) {
  if (isPermanentStage) return `${name}.${domain}`;
  return `${name}-${domain}`;
}

const router = isPermanentStage
  ? new sst.aws.Router("MyRouter", {
     domain: {
       name: domain,
       aliases: [`*.${domain}`]
     }
   })
  : sst.aws.Router.get("MyRouter", "A2WQRGCYGTFB7Z");

// Frontend
const web = new sst.aws.Nextjs("MyWeb", {
  path: "packages/web",
  router: {
    instance: router
  }
});

// API
const api = new sst.aws.Function("MyApi", {
  handler: "packages/functions/api.handler",
  url: {
    router: {
      instance: router,
      path: "/api"
    }
  }
});

// Docs
const docs = new sst.aws.Astro("MyDocs", {
  path: "packages/docs",
  router: {
    instance: router,
    domain: subdomain("docs")
  }
});

Our components are all sharing the same CloudFront distribution. We also have our PR stages sharing the same router as our dev stage.


Www Src Content Docs Docs Console (`www/src/content/docs/docs/console.mdx`)


title: Console

import { Image } from "astro:assets"
import { Tabs, TabItem } from '@astrojs/starlight/components';

import consoleHomeLight from '../../../assets/docs/console/sst-console-home-light.png';
import consoleHomeDark from '../../../assets/docs/console/sst-console-home-dark.png';
import consoleLogsLight from '../../../assets/docs/console/sst-console-logs-light.png';
import consoleLogsDark from '../../../assets/docs/console/sst-console-logs-dark.png';
import consoleIssuesLight from '../../../assets/docs/console/sst-console-issues-light.png';
import consoleIssuesDark from '../../../assets/docs/console/sst-console-issues-dark.png';
import consoleLocalLight from '../../../assets/docs/console/sst-console-local-light.png';
import consoleLocalDark from '../../../assets/docs/console/sst-console-local-dark.png';
import consoleResourcesLight from '../../../assets/docs/console/sst-console-resources-light.png';
import consoleResourcesDark from '../../../assets/docs/console/sst-console-resources-dark.png';
import consoleUpdatesLight from '../../../assets/docs/console/sst-console-updates-light.png';
import consoleUpdatesDark from '../../../assets/docs/console/sst-console-updates-dark.png';
import consoleAutodeployLight from '../../../assets/docs/console/sst-console-autodeploy-light.png';
import consoleAutodeployDark from '../../../assets/docs/console/sst-console-autodeploy-dark.png';

The Console is a web based dashboard to manage your SST apps β€” console.sst.dev

With it, you and your team can see all your apps, their resources and updates, view logs, get alerts on any issues, and git push to deploy them.

:::tip
The Console is completely optional and comes with a free tier.
:::


Get started

Start by creating an account and connecting your AWS account.

:::note
Currently the Console only supports apps deployed to AWS.
:::

  1. Create an account with your email

    It's better to use your work email so that you can invite your team to your workspace later β€” console.sst.dev

  2. Create a workspace

    You can add your apps and invite your team to a workspace. A workspace can be for a personal project or for your team at work. You can create as many workspaces as you want.

    :::tip
    Create a workspace for your organization. You can use it to invite your team and connect all your AWS accounts.
    :::

  3. Connect your AWS account

    This will ask you to create a CloudFormation stack in your AWS account. Make sure that this stack is being added to us-east-1. Scroll down and click Create stack.

    :::caution
    The CloudFormation stack needs to be created in us-east-1. If you create it in the wrong region by mistake, remove it and create it again.
    :::

    This stack will scan all the regions in your account for SST apps and subscribe to them. Once created, you'll see all your apps, stages, and the functions in the apps.

    If you are connecting a newly created AWS account, you might run into the following error while creating the stack.

    Resource handler returned message: "Specified ReservedConcurrentExecutions for function decreases account's UnreservedConcurrentExecution below its minimum value

    This happens because AWS has been limiting the concurrency of Lambda functions for new accounts. It's a good idea to increase this limit before you go to production anyway.

    To do so, you can request a quota increase to the default value of 1000. You can also do the following to expedite the request.

    Expedite the request
    If you want to expedite the request:

    1. Submit the request.
    2. Click the Quota request history link in the sidebar.
    3. Click on AWS Support Center Case to open your request case details.
    4. Hit the Reply button and select Chat to chat with an AWS representative to expedite it.
  4. Invite your team

    Use the email address of your teammates to invite them. They just need to login with the email you've used and they'll be able to join your workspace.


How it works

At a high level, here's how the Console works.

  • It's hosted on our side

    It stores some metadata about what resources you have deployed. We'll have a version that can be self-hosted in the future.

  • You can view all your apps and stages

    Once you've connected your AWS accounts, it'll deploy a separate CloudFormation stack and connect to any SST apps in it. And all your apps and stages will show up automatically.

  • It's open-source and built with SST

    The Console is an SST app. You can view the source on GitHub. It's also auto-deployed using itself.


Security

The CloudFormation stack that the Console uses, creates an IAM Role in your account to manage your resources. If this is a concern for your production environments, we have a couple of options.

By default, this role is granted AdministratorAccess, but you can customize it to restrict access. We'll look at this below. Additionally, if you'd like us to sign a BAA, feel free to contact us.

There maybe cases where you don't want any data leaving your AWS account. For this, we'll be supporting self-hosting the Console in the future.


IAM permissions

Permissions for the Console fall into two categories: read and write:

  • Read Permissions: The Console needs specific permissions to display information about resources within your SST apps.

    Purpose AWS IAM Action
    Fetch stack outputs cloudformation:DescribeStacks
    Retrieve function runtime and size lambda:GetFunction
    Access stack metadata ec2:DescribeRegions``s3:GetObject``s3:ListBucket
    Display function logs logs:DescribeLogStreams``logs:FilterLogEvents``logs:GetLogEvents``logs:StartQuery
    Monitor invocation usage cloudwatch:GetMetricData

    Attach the arn:aws:iam::aws:policy/ReadOnlyAccess AWS managed policy to the IAM Role for comprehensive read access.

  • Write Permissions: The Console requires the following write permissions.

    Purpose AWS IAM Action
    Forward bootstrap bucket events to event bus s3:PutBucketNotification
    Send events to Console events:PutRule``events:PutTargets
    Grant event bus access for Console iam:CreateRole``iam:DeleteRole``iam:DeleteRolePolicy``iam:PassRole``iam:PutRolePolicy
    Enable Issues to subscribe logs logs:CreateLogGroup``logs:PutSubscriptionFilter
    Invoke Lambda functions and replay invocations lambda:InvokeFunction

It's good practice to periodically review and update these policies.


Customize policy

To customize IAM permissions for the CloudFormation stack:

  1. On the CloudFormation create stack page, download the default template.json.

  2. Edit the template file with necessary changes.

    View the template changes

    text

/* Detailed source-code truncated for AI context efficiency. */

text
3. Upload your edited `template.json` file to an S3 bucket.

4. Return to the CloudFormation create stack page and replace the template URL in the page URL.

---

## Pricing

[Starting Feb 1, 2025](/blog/console-pricing-update), the Console will be priced based on the number of active resources in your SST apps.

| Resources | Rate per resource |
|-----------|-----------|
| First 2000   | $0.086  |
| 2000+        | $0.032 |

**Free Tier**: Workspaces with 350 active resources or fewer.

So for example, if you go over the free tier and have 351 active resources in a month, your bill will be 351 x $0.086 = $30.2.

A couple of things to note.

- These are calculated for a given workspace every month.
- A resource is what SST creates in your cloud provider. [Learn more below](#faq).
- You can always access personal stages, even if you're above the free tier.
- A resource is considered active if it comes from a stage:
  - That has been around for at least 2 weeks.
  - And, was updated during the month.
- For volume pricing, feel free to [contact us][contact-us].

[Learn more in the FAQ](#faq).

---

##### Active resources

A resource is considered active if it comes from a stage that has been around for at least 2 weeks. And, was updated during the month.

Let's look at a few different scenarios to see how this works.

- A stage that was created 5 months ago and was deployed this month, is active.
- A stage that was created 5 months ago but was not deployed this month, is not active.
- A stage that was created 12 days ago, is not active.
- A stage that was created 20 days ago and was removed 10 days ago, is not active.
- A stage that was created 5 months ago, deployed this month, then removed this month, is active.
- A stage created 5 months ago, was not deployed this month, and removed this month, is not active.

---

#### Old pricing

Previously, the Console pricing was based on the number of times the Lambda functions in your SST apps are invoked per month and it used the following tiers.

| Invocations | Rate (per invocation) |
|-------------|------|
| First 1M    | Free |
| 1M - 10M    | $0.00002 |
| 10M+        | $0.000002 |

- These are calculated for a given workspace on a monthly basis.
- This does not apply to personal stages, they'll be free forever.
- There's also a soft limit for Issues on all accounts.
- For volume pricing, feel free to [contact us][contact-us].

---

## Features

Here are a few of the things the Console does for you.

1. [**Logs**](#logs): View logs from any log group in your app
2. [**Issues**](#issues): Get real-time alerts for any errors in your app
3. [**Local logs**](#local-logs): View logs from your local `sst dev` session
4. [**Updates**](#updates): View the details of every update made to your app
5. [**Resources**](#resources): View all the resources in your app and their props
6. [**Autodeploy**](#autodeploy): Auto-deploy your app when you _git push_ to your repo

---

### Logs

With the Console, you don't need to go to CloudWatch to look at the logs for your functions, containers and other log groups. You can view:

- View recent logs
- Jump to a specific time
- Search for logs with a given string


  
  
  


---

### Issues

The Console will automatically show you any errors in your Node.js Lambda functions and containers in real-time. And notify you through Slack or email.


  
  
  


With Issues, there is:

- **Nothing to setup**, no code to instrument
- **Source maps** are supported **automatically**
- **No impact on performance**, since your code isn't modified

:::note
Issues works out of the box and has no impact on performance.
:::

Issues currently only supports Node.js functions and containers. Other languages and runtimes are on the roadmap.

---

#### Error detection

For the Console to automatically report your errors, you need to `console.error` an error object.

```js title="src/index.ts"
console.error(new Error("my-error"));

This works a little differently for containers and functions.

  • Containers

    In a container applications, your code needs to also import the SST JS SDK.

    js title="src/index.ts" {1}
    import "sst";
    
    console.error(new Error("my-error"));

    This applies a polyfill to the console object to prepend the log lines with a marker that allows Issues to detect errors. More on this below.

    If you are already importing the SDK, you won't need to add an additional import.

  • Functions

    In addition, to errors logged using console.error(new Error("my-error")), Issues also reports Lambda function failures.

    js title="src/lambda.ts"
    console.error(new Error("my-error"));

    In Lambda you don't need to import the SDK to polyfill the console object. Since the Lambda runtime does this automatically for you.


How it works

Here's how Issues works behind the scenes.

  1. When an app is deployed or when an account is first synced, we add a log subscriber to the CloudWatch Log groups in your SST apps.
    • This is added to your AWS account and includes a Lambda function. More on this below.
  2. If the subscriber filter matches anything that looks like an error it invokes the Lambda function.
    • In case of errors from a Lambda function, the Lambda runtime automatically adds a marker to the logs that the filter matches for.
    • For containers, the SST SDK polyfills the console object to add the marker.
  3. The Lambda function tries to parse the error. If the error comes from a Lambda function, it fetches the source maps from the state bucket in your account.
  4. It then hits an endpoint in the SST Console and passes in that error.
  5. Finally, the Console groups similar looking errors together and displays them.

Log subscriber

The log subscriber also includes the following:

  1. Lambda function that'll be invoked when a log with an error is matched.
    • This function has a max concurrency set to 10.
    • If it falls behind on processing by over 10 minutes, it'll discard the logs.
    • This prevents it from scaling indefinitely when there's a burst of errors.
    • This also means that if there are a lot of errors, the alerts might be delayed by up to 10 minutes.
  2. IAM role that gives it access to query the logs and the state bucket for the source maps.
  3. Log group with a 1 day retention.

These are added to every region in your AWS account that has a CloudWatch log group from your SST apps. It's deployed using a CloudFormation stack.

This process of adding a log subscriber might fail if we:

  • Don't have enough permissions. In this case, update the permissions that you've granted to the Console.
  • Hit the limit for the number of subscribers, there's a maximum of 2 subscribers. To fix this, you can remove one of the existing subscribers.

You can see these errors in the Issues tab. Once you've fixed these issues, you can hit Retry and it'll try attaching the subscriber again.


Costs

AWS will bill you for the Lambda function log subscriber that's in your account. This is usually fairly minimal.

Even if your apps are generating an infinite number of errors, the Lambda function is limited to a concurrency of 10. So the maximum you'll be charged $43 x 10 = $430 per month x # of regions that are being monitored.

You can also disable Issues from your workspace settings, if you are using a separate service for monitoring.

Learn more about Lambda pricing.


Updates

Each update in your app also gets a unique URL, a permalink. This is printed out by the SST CLI.

bash title="sst deploy"
β†—  Permalink  https://sst.dev/u/318d3879

You can view these updates in the Console. Each update shows:

  1. Full list of all the resources that were modified
  2. Changes in their inputs and outputs
  3. Any Docker or site builds logs
  4. CLI command that triggered the update
  5. Git commit, if it was an auto-deploy

The permalink is useful for sharing with your team and debugging any issues with your deploys.

The CLI updates your state with the event log from each update and generated a globally unique id. If your AWS account is connected to the Console, it'll pull the state and event log to generate the details for the update permalink.

When you visit the permalink, the Console looks up the id of the update and redirects you to the right app in your workspace.


Resources

The Console shows you the complete state of the resources in your app. You can view:

  1. Each resource in your app
  2. The relation between resources
  3. The outputs of a given resource

Autodeploy

The Console can auto-deploy your apps when you git push to your GitHub repo. Autodeploy uses AWS CodeBuild in your account to run the build.

We designed Autodeploy to be a better fit for SST apps when compared to alternatives like GitHub Actions or CircleCI.

  1. Easy to get started
    • Autodeploy supports the standard branch and PR workflow out of the box. You don't need a config file to get started.
    • There are no complicated steps in configuring your AWS credentials; since your AWS account is already connected to the Console.
  2. Configurable
    • You can configure how Autodeploy works directly through your sst.config.ts.
    • It's typesafe and the callbacks let you customize how to respond to incoming git events.
  3. Runs in your AWS account
    • The builds are run in your AWS account.
    • It can also be configured to run in your VPC. This is useful if your builds need to access private resources.
  4. Integrates with the Console
    • You can see which resources were updated in a deploy.
    • Your resource updates will also show you the related git commit.

Setup

To get started with Autodeploy:

  1. Enable the GitHub integration

    Head over to your Workspace settings > Integrations and enable GitHub. This will ask you to login to GitHub and you'll be asked to pick the GitHub organization or user you want to link to.

    :::tip
    You can only associate your workspace with a single GitHub org.
    :::

    If you have multiple GitHub orgs, you can create multiple workspaces in the Console.

  2. Connect a repo

    To auto-deploy an app, head over to the App's Settings > Autodeploy and select the repo for the app.

  3. Configure an environment

    Next you can configure a branch or PR environment by selecting the stage you want deployed to an AWS account. You can optionally configure environment variables as well.

    :::note
    Stage names by default are generated based on the branch or PR.
    :::

    By default, stages are based on the branch name or PR. We'll look at this in detail below.

  4. Git push

    Finally, git push to the environment you configured and head over to your app's Autodeploy tab to see it in action.

    :::note
    PR stages are removed when the PR is closed while branch stages are not.
    :::

    For example, if you configure a branch environment for the stage production, any git pushes to the production branch will be auto-deployed. Similarly, if you create a new PR, say PR#12, the Console will auto-deploy a stage called pr-12.

    You can also manually trigger a deployment through the Console by passing in a Git ref and the stage you want to deploy to.

  5. Setup alerts

    Once your deploys are working, you can set the Console to send alerts for your deploys. Head over to your Workspace Settings > Alerts and add a new alert to be notified on any Autodeploys, or only on Autodeploy errors.

:::tip
You can configure how Autodeploy works through your sst.config.ts.
:::

While Autodeploy supports the standard branch and PR workflow out of the box, it can be configured in depth through your sst.config.ts.


Configure

The above can be configured through the console.autodeploy option in the sst.config.ts.

ts title="sst.config.ts" {7-15}
export default $config({
  // Your app's config
  app(input) { },
  // Your app's resources
  async run() { },
  // Your app's Console config
  console: {
    autodeploy: {
      target(event) {
        if (event.type === "branch" && event.branch === "main" && event.action === "pushed") {
          return { stage: "production" };
        }
      }
    }
  }
});

In the above example we are using the console.autodeploy.target option to change the stage that's tied to a git event. Only git pushes to the main branch to auto-deploy to the production stage.

This works because if target returns undefined, the deploy is skipped. And if you provide your own target callback, it overrides the default behavior.

:::tip
You can use the git events to configure how your app is auto-deployed.
:::

Through the console.autodeploy.runner option, you can configure the runner that's used. For example, if you wanted to increase the timeouts to 2 hours, you can.

ts title="sst.config.ts"
console: {
  autodeploy: {
    runner: { timeout: "2 hours" }
  }
}

This also takes the stage name, so you can set the runner config for a specific stage.

ts title="sst.config.ts"
console: {
  autodeploy: {
    runner(stage) {
      if (stage === "production") return { timeout: "3 hours" };
    }
  }
}

You can also have your builds run inside your VPC.

ts title="sst.config.ts"
console: {
  autodeploy: {
    runner: {
      vpc: {
        id: "vpc-0be8fa4de860618bb",
        securityGroups: ["sg-0399348378a4c256c"],
        subnets: ["subnet-0b6a2b73896dc8c4c", "subnet-021389ebee680c2f0"]
      }
    }
  }
}

Or specify files and directories to be cached.

ts title="sst.config.ts"
console: {
  autodeploy: {
    runner: {
      cache: {
        paths: ["node_modules", "/path/to/cache"]
      }
    }
  }
}

Read more about the console.autodeploy config.


Environments

The Console needs to know which account it needs to autodeploy into. You configure this under the App's Settings > Autodeploy. Each environment takes:

  1. Stage

    The stage that is being deployed. By default, the stage name comes from the name of the branch. Branch names are sanitized to only letters/numbers and hyphens. So for example:

    • A push to a branch called production will deploy a stage called production.
    • A push to PR#12 will deploy to a stage called pr-12.

    As mentioned, above you can customize this through your sst.config.ts.

    :::tip
    You can specify a pattern to match the stage name in your environments.
    :::

    If multiple stages share the same environment, you can use a glob pattern. For example, pr-* matches all stages that start with pr-.

  2. AWS Account

    The AWS account that you are deploying to.

  3. Environment Variables

    Any environment variables you need for the build process. These are made available under process.env.* in your sst.config.ts.


How it works

When you git push to a branch, pull request, or tag, the following happens:

  1. The stage name is generated based on the console.autodeploy.target callback.
    1. If there is no callback, the stage name is a sanitized version of the branch or tag.
    2. If there is a callback but no stage is returned, the deploy is skipped.
  2. The stage is matched against the environments in the Console to get the AWS account and any environment variables for the deploy.
  3. The runner config is generated based on the console.autodeploy.runner. Or the defaults are used.
  4. The deploy is run based on the above config.

This only applies only to git events. If you trigger a deploy through the Console, you are asked to specify the stage you want to deploy to. So in this case, it skips step 1 from above and does not call console.autodeploy.target.

Both target and runner are optional and come with defaults, but they can be customized.


Costs

AWS will bill you for the CodeBuild build minutes that are used to run your builds. Learn more about CodeBuild pricing.


Local logs

When the Console starts up, it checks if you are running sst dev locally. If so, then it'll show you real-time logs from your local terminal. This works by connecting to a local server that's run as a part of the SST CLI.

:::info
The local server only allows access from localhost and console.sst.dev.
:::

The local logs works in all browsers and environments. But for certain browsers like Safari or Brave, and Gitpod, it needs some additional configuration.


Safari & Brave

Certain browsers like Safari and Brave require the local connection between the browser and the sst dev CLI to be running on HTTPS.

SST can automatically generate a locally-trusted certificate using the sst cert command.

bash
sst cert

You'll only need to run this once on your machine.


Gitpod

If you are using Gitpod, you can use the Gitpod Local Companion app to connect to the sst dev process running inside your Gitpod workspace.

To get started:

  1. Install Gitpod Local Companion app
  2. Run the Companion app
  3. Navigate to Console in the browser

The companion app runs locally and creates a tunnelled connection to your Gitpod workspace.


FAQ

Here are some frequently asked questions about the Console.

  • Do I need to use the Console to use SST?

    You don't need the Console to use SST. It compliments the CLI and has some features that help with managing your apps in production.

    That said, it is completely free to get started. You can create an account and invite your team, without having to add a credit card.

  • Is there a free tier?

    If your workspace has 350 active resources or fewer for the month, it's considered to be in the free tier. This count also resets every month.

  • What happens if I go over the free tier?

    You won't be able to access the production or deployed stages till you add your billing details in the workspace settings.

    Note that, you can continue to access your personal stages. Just make sure you have sst dev running locally. Otherwise the Console won't be able to detect that it's a personal stage.

  • What counts as a resource?

    Resources are what SST creates in your cloud provider. This includes the resources created by both SST's built-in components, like Function, Nextjs, Bucket, and the ones created by any other Terraform/Pulumi provider.

    Some components, like Nextjs and StaticSite, create multiple resources. In general, the more complex the component, the more resources it'll create.

    You can see a full list of resources if you go to an app in your Console and navigate to a stage in it.

    For some context, the Console is itself a pretty large SST app and it has around 320 resources.

  • Do PR stages also count?

    A stage has to be around for at least 2 weeks before the resources in it are counted as active. So if a PR stage is created and removed within 2 weeks, they don't count.

    However, if you remove a stage and create a new one with the same name, it does not reset the 2 week initial period.


Old pricing FAQ

Here were some frequently asked questions about the old pricing plan for the Console.

  • Do I need to switch to the new pricing?

    If you are currently on the old plan, you don't have to switch and you won't be automatically switched over either.

    You can go to the workspace settings and check out how much you'll be billed based on both the plans. To switch over, you can cancel your current plan and then subscribe to the new plan.

    At some point in the future, we'll remove the old plan. But there's no specific timeline for it yet.

  • Which Lambda functions are included in the number of invocations?

    The number of invocations are only counted for the Lambda functions in your SST apps. Other Lambda functions in your AWS accounts are not included.

  • Do the functions in my personal stages count as a part of the invocations?

    Lambda functions that are invoked locally are not included.

  • My invocation volume is far higher than the listed tiers. Are there any other options?

    Feel free to contact us and we can figure out a pricing plan that works for you.

If you have any further questions, feel free to send us an email.


Www Src Content Docs Docs Custom Domains (`www/src/content/docs/docs/custom-domains.mdx`)


title: Custom Domains

import { Tabs, TabItem } from '@astrojs/starlight/components';

You can configure custom domains and subdomains for your frontends, APIs, services, or routers in SST.

:::note
SST currently supports configuring custom domains for AWS components.
:::

By default, these components auto-generate a URL. You can pass in the domain to use your custom domain.

```ts title="sst.config.ts" {2} new sst.aws.Nextjs("MyWeb", { domain: "example.com" }); ``` ```ts title="sst.config.ts" {2} new sst.aws.ApiGatewayV2("MyApi", { domain: "api.example.com" }); ``` ```ts title="sst.config.ts" {6} const vpc = new sst.aws.Vpc("MyVpc");

new sst.aws.Cluster("MyCluster", {
vpc,
loadBalancer: {
domain: "example.com"
}
});

text
</TabItem>
<TabItem label="Router">
```ts title="sst.config.ts" {2}
new sst.aws.Router("MyRouter", {
  domain: "example.com"
});

SST supports a couple of DNS providers automatically. These include AWS Route 53, Cloudflare, and Vercel. Other providers will need to be manually configured.

We'll look at how it works below.


Redirect www to apex domain

A common use case is to redirect www.example.com to example.com. You can do this by:

ts title="sst.config.ts" {3,4}
new sst.aws.Router("MyRouter", {
  domain: {
    name: "example.com",
    redirects: ["www.example.com"]
  }
});

Add subdomains

You can add subdomains to your domain. This is useful if you want to use a Router to route a subdomain to a separate resource.

ts title="sst.config.ts" {3,4,11}
const router = new sst.aws.Router("MyRouter", {
  domain: {
    name: "example.com",
    aliases: ["*.example.com"]
  }
});

new sst.aws.Nextjs("MyWeb", {
  router: {
    instance: router,
    domain: "docs.example.com"
  }
});

Here if a user visits docs.example.com, they'll kept on the alias domain and be served the docs site.

:::tip
You can use the Router component to centrally manage domains and routing for your
app. Learn more.
:::

However, this does not match docs.dev.example.com. For that, you'll need to add *.dev.example.com as an alias.


How it works

Configuring a custom domain is a two step process.

  1. Validate that you own the domain. For AWS you do this by creating an ACM certificate and validating it by:
    • Setting a DNS record with your domain provider.
    • Verifying through an email sent to the domain owner.
  2. Add the DNS records to route your domain to your component.

SST can perform these steps automatically for the supported providers through a concept of adapters. These create the above DNS records on a given provider.


Adapters

You can use a custom domain hosted on any provider. SST supports domains on AWS, Cloudflare, and Vercel automatically.


AWS

By default, if you set a custom domain, SST assumes the domain is configured in AWS Route 53 in the same AWS account.

js
{
  domain: {
    name: "example.com"
  }
}

This is the same as using the sst.aws.dns adapter.

js
{
  domain: {
    name: "example.com",
    dns: sst.aws.dns()
  }
}

If you have the same domain in multiple hosted zones in Route 53, you can specify the hosted zone.

js {5}
{
  domain: {
    name: "example.com",
    dns: sst.aws.dns({
      zone: "Z2FDTNDATAQYW2"
    })
  }
}

If your domains are hosted on AWS but in a separate AWS account, you'll need to follow the manual setup.


Vercel

If your domains are hosted on Vercel, you'll need to do the following.

  1. Add the Vercel provider to your app.

    bash
    sst add vercel
  2. Set the VERCEL_API_TOKEN in your environment. You might also need to set the VERCEL_TEAM_ID if the domain belongs to a team.

    bash
    export VERCEL_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
  3. Use the sst.vercel.dns adapter.

    js
    {
      domain: {
        name: "example.com",
        dns: sst.vercel.dns()
      }
    }

Cloudflare

If your domains are hosted on Cloudflare, you'll need to do the following.

  1. Add the Cloudflare provider to your app.

    bash
    sst add cloudflare
  2. Set the CLOUDFLARE_API_TOKEN in your environment.

    bash
    export CLOUDFLARE_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
    export CLOUDFLARE_DEFAULT_ACCOUNT_ID=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa

    To get your API tokens, head to the API Tokens section of your Cloudflare dashboard and create one with the Edit zone DNS policy.

    The Cloudflare providers need these credentials to deploy your app in the first place, which means they can't be set using the sst secret CLI.

    If you are auto-deploying your app through the SST Console or through your CI, you'll need to set these as environment variables.

  3. Use the sst.cloudflare.dns adapter.

    js
    {
      domain: {
        name: "example.com",
        dns: sst.cloudflare.dns()
      }
    }

Manual setup

If your domain is on a provider that is not supported above, or is in a separate AWS account; you'll need to verify that you own the domain and set up the DNS records on your own.

To manually set up a domain on an unsupported provider, you'll need to:

  1. Validate that you own the domain by creating an ACM certificate. You can either validate it by setting a DNS record or by verifying an email sent to the domain owner.

    :::note
    For CloudFront distributions, the certificate needs to be created in us-east-1.
    :::

    If you are configuring a custom domain for a CloudFront distribution, the ACM certificate that's used to prove that you own the domain needs be created in the us-east-1 region.

    For all the other components, like ApiGatewayV2 or Cluster, can be created in any region.

  2. Once validated, set the certificate ARN as the cert and set dns to false.

    js
    {
      domain: {
        name: "domain.com",
        dns: false,
        cert: "arn:aws:acm:us-east-1:112233445566:certificate/3a958790-8878-4cdc-a396-06d95064cf63"
      }
    }
  3. Add the DNS records in your provider to point to the CloudFront distribution, API Gateway, or load balancer URL.


Www Src Content Docs Docs Environment Variables (`www/src/content/docs/docs/environment-variables.mdx`)


title: Environment Variables

You can manage the environment variables for all the components in your app, across all your stages, through the sst.config.ts.

:::tip
You don't need to use .env files in SST.
:::

While SST automatically loads your environment variables and .env files; we don't recommend relying on them.


Typically, you'll use environment variables or .env files to share things like database URLs, secrets, or other config.

To understand why we don't recommend .env files, let's look at each of these in detail.


A very common use case for .env is to share something like a database URL across your app.

Instead in SST, you can link the resources together.

ts title="sst.config.ts" {4}
const rds = new sst.aws.Postgres("MyPostgres");

new sst.aws.Nextjs("MyWeb", {
  link: [rds]
});

You can then access the database in your Next.js app with the JS SDK.

ts title="app/page.tsx" {5-7}
import { Resource } from "sst";

export const db = drizzle(client, {
  schema,
  database: Resource.MyPostgres.database,
  secretArn: Resource.MyPostgres.secretArn,
  resourceArn: Resource.MyPostgres.clusterArn
});

This has a couple of key advantages:

  1. You don't have to deploy your database separately and then store the credentials in a .env file.
  2. You don't need to update this for every stage.
  3. You don't have to share these URLs with your teammates.

Anybody on your team can just run sst deploy on any stage and it'll deploy the app and link the resources.

:::tip
Your team can just git checkout and sst deploy, without the need for a separate .env file.
:::

You can learn more about linking resources.


Secrets

Another common use case for .env is to manage secrets across your app.

SST has a built-in way to handle secrets.

ts title="sst.config.ts" {4}
const secret = new sst.Secret("MySecret");

new sst.aws.Nextjs("MyWeb", {
  link: [secret]
});

You can set the secret using the sst secret CLI.

bash title="terminal"
sst secret set MySecret my-secret-value

This far more secure than storing it in a .env file and accidentally committing it to Git.

Learn more about secrets.


Other config

Finally, people use .env files for some general config. These are often different across stages and are not really sensitive. For example, you might have your SENTRY_DSN that's different for dev and prod.

We recommend putting these directly in your sst.config.ts instead. And using the right one based on the stage.

ts title="sst.config.ts"
const SENTRY_DSN = $app.stage !== "prod"
   ? "https://[email protected]/bar"
   : "https://[email protected]/qux";

You can also conditionally set it based on if you are running sst dev or sst deploy.

ts title="sst.config.ts"
const SENTRY_DSN = $dev === true
   ? "https://[email protected]/bar"
   : "https://[email protected]/qux";

And you can pass this into your frontends and functions.

ts title="sst.config.ts" {3}
new sst.aws.Nextjs("MyWeb", {
  environment: {
    SENTRY_DSN
  }
});

Learn more about $app and $dev.


Traditional

As mentioned above, SST also supports the traditional approach. If you run sst dev or sst deploy with an environment variable:

bash title="terminal" "some_env_var=foo"
SOME_ENV_VAR=FOO sst deploy

You can access it using the process.env in your sst.config.ts.

ts title="sst.config.ts"
async run() {
  console.log(process.env.SOME_ENV_VAR); // FOO
}

However, this isn't automatically added to your frontends or functions. You'll need to add it manually.

ts title="sst.config.ts" {3}
new sst.aws.Nextjs("MyWeb", {
   environment: {
     SOME_ENV_VAR: process.env.SOME_ENV_VAR ?? "fallback value",
   }
});

SST doesn't do this automatically because you might have multiple frontends or functions and you might not want to load it for all of them.

:::tip
Environment variables are not automatically added to your frontend or functions.
:::

Now you can access it in your frontend.

ts title="app/page.tsx"
export default function Home() {
  return <p>Hello {process.env.SOME_ENV_VAR}</p>;
}

.env

The same thing works if you have a .env file in your project root.

bash title=".env"
SOME_ENV_VAR=FOO

It'll be loaded into process.env in your sst.config.ts.

ts title="sst.config.ts"
async run() {
  console.log(process.env.SOME_ENV_VAR); // FOO
}

Or if you have a stage specific .env.dev file.

bash title=".env.dev"
SOME_ENV_VAR=BAR

And you run sst deploy --stage dev, it'll be loaded into process.env in your sst.config.ts.

ts title="sst.config.ts"
async run() {
  console.log(process.env.SOME_ENV_VAR); // BAR
}

While the traditional approach works, we do not recommend it because it's both cumbersome and not secure.


Www Src Content Docs Docs Iam Credentials (`www/src/content/docs/docs/iam-credentials.mdx`)


title: IAM Credentials

SST deploys your AWS resources using your AWS credentials. In this guide we'll look at how to set these credentials, the basic set of permissions it needs, and how to customize it.


Credentials

There are a couple of different ways to set the credentials that your app will use. The simplest is using a credentials file.

However, if you're still figuring out how to configure your AWS account, we recommend following our guide on it.


From a file

By default, your AWS credentials are in a file:

  • ~/.aws/credentials on Linux, Unix, macOS
  • C:\Users\USER_NAME\.aws\credentials on Windows

If the credentials file does not exist on your machine.

  1. Follow this to create an IAM user
  2. And then use this to configure the credentials

Below we'll look at how to customize the permissions that are granted to this user.


Your credentials file might look like:

bash title="~/.aws/credentials"
[default]
aws_access_key_id = <YOUR_ACCESS_KEY_ID>
aws_secret_access_key = <YOUR_SECRET_ACCESS_KEY>

Where default is the name of the credentials profile.

And if you have multiple credentials, it might look like:

bash title="~/.aws/credentials"
[default]
aws_access_key_id = <DEFAULT_ACCESS_KEY_ID>
aws_secret_access_key = <DEFAULT_SECRET_ACCESS_KEY>

[staging]
aws_access_key_id = <STAGING_ACCESS_KEY_ID>
aws_secret_access_key = <STAGING_SECRET_ACCESS_KEY>

[production]
aws_access_key_id = <PRODUCTION_ACCESS_KEY_ID>
aws_secret_access_key = <PRODUCTION_SECRET_ACCESS_KEY>

By default, SST uses the credentials for the default profile. To use one of the other profiles, set the profile in your sst.config.ts.

ts title="sst.config.ts"
{
  providers: {
    aws: {
      profile: "staging"
    }
  }
}

You can customize this for the stage your app is being deployed to.

ts title="sst.config.ts"
app(input) {
  return {
    // ...
    providers: {
      aws: {
        profile: input?.stage === "staging" ? "staging" : "default"
      }
    }
  };
},

If you've configured AWS credentials previously through the AWS_PROFILE environment variable or through a .env file, it will override the profile set in your sst.config.ts. So make sure to remove any references to AWS_PROFILE.


From environment variables

SST can also detect AWS credentials in your environment and use them to deploy.

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

If you are using temporary credentials, you can also set the AWS_SESSION_TOKEN.

This is useful when you are deploying through a CI environment and there are no credential files around.


Precedence

If you have AWS credentials set in multiple places, SST will first look at:

  1. Environment variables

    This includes AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, or AWS_SESSION_TOKEN, and AWS_PROFILE. This also includes environment variables set in a .env file.

  2. SST config

    Then it'll check for the credentials or profile in your sst.config.ts.

  3. AWS config

    It'll then check for the [default] profile in your ~/.aws/config or C:\Users\USER_NAME\.aws\config.

  4. Credential files

    Finally, it'll look for any static credentials in your ~/.aws/credentials or C:\Users\USER_NAME\.aws\credentials.


IAM permissions

The credentials above are for an IAM user and it comes with an IAM policy. This defines what resources the given user has access to. By default, we are using AdministratorAccess. This gives your user complete access.

However, if you are using SST at your company, you want to secure these permissions. Here we'll look at exactly what SST needs and how you can go about customizing it.


Let's start with an IAM policy you can copy and paste.

Copy IAM Policy

text
/* Detailed source-code truncated for AI context efficiency. */

This list roughly breaks down into the following:

  1. Permissions needed to bootstrap SST in your AWS account
  2. Permissions needed to deploy your app
  3. Permissions needed by the CLI

Let's look at them in detail.


Bootstrap

SST needs to bootstrap each AWS account, in each region, once. This happens automatically when you run sst deploy or sst dev.

There are a couple of different things being bootstrapped and these are the permissions they need:

  • Permissions to create the bootstrap bucket for storing state.

    json
    {
      "Sid": "ManageBootstrapStateBucket",
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:PutBucketVersioning",
        "s3:PutBucketNotification",
        "s3:PutBucketPolicy",
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:ListBucketVersions",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::sst-state-*"
      ]
    }
  • Permissions to create the bootstrap bucket for storing the assets in your app. These include the Lambda function bundles and static assets in your frontends.

    json
    {
      "Sid": "ManageBootstrapAssetBucket",
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:PutBucketVersioning",
        "s3:PutBucketNotification",
        "s3:PutBucketPolicy",
        "s3:DeleteObject",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::sst-asset-*"
      ]
    }
  • Permissions to create the bootstrap ECR repository for hosting the Docker images in your app.

    json
    {
        "Sid": "ManageBootstrapECRRepo",
        "Effect": "Allow",
        "Action": [
          "ecr:CreateRepository",
          "ecr:DescribeRepositories"
        ],
        "Resource": [
          "arn:aws:ecr:REGION:ACCOUNT:repository/sst-asset"
        ]
    }
  • Permissions to create the bootstrap SSM parameter. This parameter stores information about the deployed bootstrap resources.

    json
    {
      "Sid": "ManageBootstrapSSMParameter",
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "ssm:PutParameter"
      ],
      "Resource": [
        "arn:aws:ssm:REGION:ACCOUNT:parameter/sst/passphrase/*",
        "arn:aws:ssm:REGION:ACCOUNT:parameter/sst/bootstrap"
      ]
    }

Deploy

The permissions that SST needs to deploy the resources in your app, depends on what you have in your app.

The following block is placed as a template in the IAM policy above for you to customize.

json
{
  "Sid": "Deployments",
  "Effect": "Allow",
  "Action": [
    "*"
  ],
  "Resource": [
    "*"
  ]
}

Below we'll look at how you can try customizing this.


CLI

The SST CLI also makes some AWS SDK calls to your account. Here are the IAM permissions it needs.

  • Permissions to manage your secrets.

    json
    {
      "Sid": "ManageSecrets",
      "Effect": "Allow",
      "Action": [
        "ssm:DeleteParameter",
        "ssm:GetParameter",
        "ssm:GetParameters",
        "ssm:GetParametersByPath",
        "ssm:PutParameter",
        "ssm:AddTagsToResource",
        "ssm:ListTagsForResource"
      ],
      "Resource": [
        "arn:aws:ssm:us-east-1:112233445566:parameter/sst/*"
      ]
    }
  • And permissions to connect to the AppSync endpoint in sst dev to run your functions Live.

    json
    {
      "Sid": "LiveLambdaSocketConnection",
      "Effect": "Allow",
      "Action": [
        "appsync:EventSubscribe",
        "appsync:EventPublish",
        "appsync:EventConnect"
      ],
      "Resource": [
        "*"
      ]
    }

Minimize permissions

Editing the above policy based on the resources you are adding to your app can be tedious. Here's an approach to consider.

  • Sandbox accounts

    Start by creating separate AWS accounts for your teammates for their dev usage. In these sandbox accounts, you can grant AdministratorAccess. This avoids having to modify their permissions every time they make some changes.

  • IAM Access Analyzer

    For your staging accounts, you can start by granting a broad permissions policy. Then after deploying your app and allowing it to run for a period of time. You can use your CloudTrail events to identify the actions and services used by that IAM user. The IAM Access Analyzer can then generate an IAM policy based on this activity, which you can use to replace the original policy.

    You can now use this for your production accounts. Learn more about how to use the IAM Access Analyzer.

In general, you want to make sure you audit the IAM permissions you are granting on a regular basis.


Www Src Content Docs Docs Import Resources (`www/src/content/docs/docs/import-resources.mdx`)


title: Import Resources

Importing is the process of bringing some previously created resources into your SST app. This'll allow SST to manage them moving forward.

This is useful for when you are migrating to SST or if you had manually created some resources in the past.


How it works

SST keeps a state of your app. It contains all the resources that are managed by your app.

:::note
Once you import a resource it's managed by SST moving forward.
:::

When you import a resource, it gets added to this state. This means that if you remove this resource in your code, it'll also remove the resource.

It's as if this resource had been created by your app.


When not to import

This is fine for most cases. But for some teams these resources might be managed by other teams. Or they are being managed by a different IaC tool. Meaning that you don't want to manage it in your app.

:::caution
Do not import resources that are being managed by another team or a different IaC tool.
:::

In these cases you should not be importing these resources. You are probably looking to reference these resources.


How to import

You import resources by passing in a property of the resource you want to import into your app. Resources have a property that you can import with and this is different for different resources. We'll look at this below.

If you are importing into an SST component, you'll need to use a transform to pass it into the underlying resource.

So let's look at two examples.

  1. Importing into an SST component
  2. Importing into a Pulumi resource

SST component

Let's start with an existing S3 Bucket with the following name.

txt
mybucket-xnbmhcvd

We want to import this bucket into the Bucket component.

  1. Start by adding the import option in the transform.

    ts title="sst.config.ts" {4}
    new sst.aws.Bucket("MyBucket", {
      transform: {
        bucket: (args, opts) => {
          opts.import = "mybucket-xnbmhcvd";
        }
      }
    });

    The transform.bucket is telling this component that instead of creating a new underlying S3 Bucket resource, we want to import an existing bucket.

    Let's deploy this.

    bash frame="none"
    sst deploy

    This will give you an error that looks something like this.

    txt frame="none"
    βœ•  Failed
       inputs to import do not match the existing resource
    
       Set the following in your transform:
       - `args.bucket = "mybucket-xnbmhcvd";`
       - `args.forceDestroy = undefined;`

    This is telling us that the resource that the Bucket component is trying to create does not match the one you are trying to import. This makes sense because you might've previously created this with a configuration that's different from what SST creates by default.

  2. Update the args

    The above error tells us exactly what we need to do next. Add the given lines to your transform.

    ts title="sst.config.ts" {4,5}
    new sst.aws.Bucket("MyBucket", {
      transform: {
        bucket: (args, opts) => {
          args.bucket = "mybucket-xnbmhcvd";
          args.forceDestroy = undefined;
    
          opts.import = "mybucket-xnbmhcvd";
        }
      }
    });

    Now if you deploy this again.

    bash frame="none"
    sst deploy

    You'll notice that the bucket has been imported.

    bash frame="none"
    |  Imported    MyBucket aws:s3:BucketV2
  3. Finally, to clean things up we can remove the import line.

    diff lang="ts" title="sst.config.ts"
    new sst.aws.Bucket("MyBucket", {
      transform: {
        bucket: (args, opts) => {
          args.bucket = "mybucket-xnbmhcvd";
          args.forceDestroy = undefined;
    
    -      opts.import = "mybucket-xnbmhcvd";
        }
      }
    });

    This bucket is now managed by your app and you can now deploy as usual.

    You do not want to remove the args changes. This matters for the args.bucket prop because the name is generated by SST. So if you remove this, SST will generate a new bucket name and remove the old one!


Pulumi resource

You might want to also import resources into your SST app that don't have a built-in SST component. In these cases, you can import them into a low-level Pulumi resource.

Let's take the same S3 Bucket example. Say you have an existing bucket with the following name.

txt
mybucket-xnbmhcvd

We want to import this bucket into the aws.s3.BucketV2 resource.

  1. Start by adding the import option.

    ts title="sst.config.ts" {6}
    new aws.s3.BucketV2("MyBucket",
      {
        objectLockEnabled: undefined
      },
      {
        import: "mybucket-xnbmhcvd"
      }
    );

    The objectLockEnabled prop here, is for illustrative purposes. We are trying to demonstrate a case where you are importing a resource in a way that it wasn't created.

    Let's deploy this.

    bash frame="none"
    sst deploy

    This will give you an error that looks something like this.

    txt frame="none"
    βœ•  Failed
       inputs to import do not match the existing resource
    
       Set the following:
       - `objectLockEnabled: undefined,`

    This is telling us that the resource that the BucketV2 component is trying to create does not match the one you are trying to import.

    This makes sense because you might've previously created this with a configuration that's different from what you are defining. Recall the objectLockEnabled prop we had added above.

  2. Update the args

    The above error tells us exactly what we need to do next. Add the given lines in your args.

    ts title="sst.config.ts" {3}
    new aws.s3.BucketV2("MyBucket",
      {
        objectLockEnabled: undefined
      },
      {
        import: "mybucket-xnbmhcvd"
      }
    );

    Now if you deploy this again.

    bash frame="none"
    sst deploy

    You'll notice that the bucket has been imported.

    bash frame="none"
    |  Imported    MyBucket aws:s3:BucketV2
  3. Finally, to clean things up we can remove the import line.

    diff lang="ts" title="sst.config.ts"
    new aws.s3.BucketV2("MyBucket",
      {
        objectLockEnabled: undefined
      },
    -  {
    -    import: "mybucket-xnbmhcvd"
    -  }
    );

    This bucket is now managed by your app and you can now deploy as usual.


Import properties

In the above examples we are importing a bucket using the bucket name. We need the bucket name because that's what AWS internally uses to do a lookup. But this is different for different resources.

So we've compiled a list of the most common resources you might import, along with the property to import them with.

You can look this up by going to the Import section of a resource's doc. For example, here's the one for a aws.s3.BucketV2.


The following table lists the properties you need to pass in to the import prop of the given resource to be able to import it.

For example, for aws.s3.BucketV2, the property is bucket name and it looks something like, some-unique-bucket-name.

Resource Property Example
aws.ec2.Vpc VPC ID vpc-a01106c2
aws.iam.Role Role name role-name
aws.sqs.Queue Queue URL https://queue.amazonaws.com/80398EXAMPLE/MyQueue
aws.sns.Topic Topic ARN arn:aws:sns:us-west-2:0123456789012:my-topic
aws.rds.Cluster Cluster identifier aurora-prod-cluster
aws.ecs.Service Cluster and service name cluster-name/service-name
aws.ecs.Cluster Cluster name cluster-name
aws.s3.BucketV2 Bucket name bucket-name
aws.kinesis.Stream Stream name my-kinesis-stream
aws.dynamodb.Table Table name table-name
aws.lambda.Function Function name function-name
aws.apigatewayv2.Api API ID 12345abcde
aws.cognito.UserPool User Pool ID us-east-1_abc123
aws.apigateway.RestApi REST API ID 12345abcde
aws.cloudwatch.LogGroup Log Group name my-log-group
aws.cognito.IdentityPool Identity Pool ID us-east-1:1a234567-8901-234b-5cde-f6789g01h2i3
aws.cloudfront.Distribution Distribution ID E74FTE3EXAMPLE

Feel free to Edit this page and submit a PR if you want to add to this list.


Www Src Content Docs Docs Linking (`www/src/content/docs/docs/linking.mdx`)


title: Linking

import { Tabs, TabItem } from '@astrojs/starlight/components';
import VideoAside from "../../../components/VideoAside.astro";

Resource Linking allows you to access your infrastructure in your runtime code in a typesafe and secure way.

  1. Create a resource that you want to link to. For example, a bucket.

    ts title="sst.config.ts" {6,11}
    const bucket = new sst.aws.Bucket("MyBucket");
  2. Link it to your function or frontend, using the link prop.

    ```ts title="sst.config.ts" {2} new sst.aws.Nextjs("MyWeb", { link: [bucket] }); ``` ```ts title="sst.config.ts" {2} new sst.aws.Remix("MyWeb", { link: [bucket] }); ``` ```ts title="sst.config.ts" {2} new sst.aws.Astro("MyWeb", { link: [bucket] }); ``` ```ts title="sst.config.ts" {3} new sst.aws.Function("MyFunction", { handler: "src/lambda.handler", link: [bucket] }); ```
  3. Use the SDK to access the linked resource in your runtime in a typesafe way.

    ```js title="app/page.tsx" import { Resource } from "sst";

    console.log(Resource.MyBucket.name);

    text
    </TabItem>
    <TabItem label="Remix">
    ```js title="app/routes/_index.tsx"
    import { Resource } from "sst";
    
    console.log(Resource.MyBucket.name);
    ```astro title="src/pages/index.astro" --- import { Resource } from "sst";

    console.log(Resource.MyBucket.name);

    text
    </TabItem>
    <TabItem label="Function">
    ```js title="src/lambda.ts"
    import { Resource } from "sst";
    
    console.log(Resource.MyBucket.name);

    :::tip
    The SDK currently supports JS/TS, Python, Golang, and Rust.
    :::

Learn how to use the SDK in Python, Golang, and Rust.


Working locally

The above applies to your app deployed through sst deploy.

To access linked resources locally you'll need to be running sst dev. By default, the sst dev CLI runs a multiplexer that also starts your frontend for you. This loads all your linked resources in the environment. Read more about sst dev.

However if you are not using the multiplexer.

bash frame="none"
sst dev --mode=basic

You'll need to wrap your frontend's dev command with the sst dev command.

```bash sst dev next dev ``` ```bash sst dev remix dev ``` ```bash sst dev astro dev ``` ```bash sst dev ```

How it works

At high level when you link a resource to a function or frontend, the following happens:

  1. The links that the resource exposes are injected into the function package.

    :::tip
    The links a component exposes are listed in its API reference. For example, you can view a Bucket's links here.
    :::

  2. The types to access these links are generated.

  3. The function is given permission to access the linked resource.


Resource links are injected into your function or frontend package when you run sst dev or sst deploy. But this is done in a slightly different way for both these cases.

Functions

The functions in SST are tree shaken and bundled using esbuild. While bundling, SST injects the resource links into the globalThis. These are encrypted and added to the function bundle. And they are synchronously decrypted on load by the SST SDK.

Frontends

The frontends are not bundled by SST. Instead, when they are built, SST injects the resource links into the process.env object using the prefix SST_RESOURCE_.

This is why when you are running your frontend locally, it needs to be wrapped in the sst dev command.

:::note
Links are only available on the server of your frontend.
:::

Resource links are only available on the server-side of your frontend. If you want to access them in your client components, you'll need to pass them in explicitly.


Generating types

When you run sst dev or sst deploy, it generates the types to access the linked resources. These are generated as:

  1. A sst-env.d.ts file in the project root with types for all the linked resources in the app.
  2. A sst-env.d.ts file in the same directory of the nearest package.json of the function or frontend that's receiving the links. This is usually just a small reference shim to the root sst-env.d.ts file.

You can check the generated sst-env.d.ts types into source control. This will let your teammates see the types without having to run sst dev when they pull your changes.


Extending linking

The examples above are built into SST's components. You might want to modify the permissions that are granted as a part of these links.

Or, you might want to link other resources from the Pulumi/Terraform ecosystem. Or want to link a different set of outputs than what SST exposes.

You can do this using the sst.Linkable component.


The Linkable component takes a list of properties that you want to link. These can be
outputs from other resources or constants.

ts title="sst.config.ts"
const myLinkable = new sst.Linkable("MyLinkable", {
  properties: { foo: "bar" }
});

You can optionally include permissions or bindings for the linked resource.

Now you can now link this resource to your frontend or a function.

ts title="sst.config.ts" {3}
new sst.aws.Function("MyApi", {
  handler: "src/lambda.handler",
  link: [myLinkable]
});

Then use the SDK to access that at runtime.

js title="src/lambda.ts"
import { Resource } from "sst";

console.log(Resource.MyLinkable.foo);

Read more about sst.Linkable.


You can also wrap any resource class to make it linkable with the Linkable.wrap static method.

ts title="sst.config.ts"
Linkable.wrap(aws.dynamodb.Table, (table) => ({
  properties: { tableName: table.name }
}));

Now you create an instance of aws.dynamodb.Table and link it in your app like any other SST
component.

ts title="sst.config.ts" {7}
const table = new aws.dynamodb.Table("MyTable", {
  attributes: [{ name: "id", type: "S" }],
  hashKey: "id"
});

new sst.aws.Nextjs("MyWeb", {
  link: [table]
});

And use the SDK to access it at runtime.

js title="app/page.tsx"
import { Resource } from "sst";

console.log(Resource.MyTable.tableName);

You can also modify the links SST creates. For example, you might want to change the permissions of a linkable resource.

ts title="sst.config.ts" "sst.aws.bucket"
sst.Linkable.wrap(sst.aws.Bucket, (bucket) => ({
   properties: { name: bucket.name },
   include: [
     sst.aws.permission({
       actions: ["s3:GetObject"],
       resources: [bucket.arn]
     })
   ]
 }));

This overrides the existing link and lets you create your own.

Read more about sst.Linkable.wrap.


If you want to pass links to compute not managed by SST, like a native ECS task definition or a Kubernetes pod, use Linkable.env(). It converts your linked resources into SST_RESOURCE_* environment variables so Resource.MyResource works at runtime.

ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket");

const environment = sst.Linkable.env([bucket]);

This returns an Output<Record<string, string>> that you can pass to any provider that accepts environment variables.

ts title="sst.config.ts"
new kubernetes.apps.v1.Deployment("MyDeployment", {
  spec: {
    template: {
      spec: {
        containers: [{
          name: "app",
          image: "my-image",
          env: sst.Linkable.env([bucket]).apply((env) =>
            // Kubernetes requires environment variables to be an array of objects
            Object.entries(env).map(([name, value]) => ({ name, value: String(value) })),
          ),
        }],
      },
    },
  },
});

Read more about sst.Linkable.env. Check out an example.


Www Src Content Docs Docs Live (`www/src/content/docs/docs/live.mdx`)


title: Live

Live is a feature of SST that lets you test changes made to your AWS Lambda functions in milliseconds. Your changes work without having to redeploy. And they can be invoked remotely.

:::tip
By default, sst dev will run all the functions in your app "live".
:::

It works by proxying requests from AWS to your local machine, executing it locally, and proxying the response back.


Advantages

This setup of running your functions locally and proxying the results back allows you to do a couple of things:

  • Your changes are reloaded in under 10ms.
  • You can set breakpoints to debug your function in your favorite IDE.
  • Functions can be invoked remotely. For example, say https://my-api.com/hello is your API endpoint. Hitting that will run the local version of that function.
    • This applies to more than just APIs. Any cron job or async event that gets invoked remotely will also run your local function.
    • It allows you to very easily debug and test webhooks, since you can just give the webhook your API endpoint.
    • Supports all function triggers, there's no need to mock an event.
  • Uses the right IAM permissions, so if a Lambda fails on AWS due to the lack of IAM permissions, it would fail locally as well.

How it works

Live uses AWS AppSync Events to communicate between your local machine and the remote Lambda function.

When you run sst dev, it bootstraps a new AppSync Events API for the region you are using.

This is roughly what the flow looks like:

  1. When you run sst dev, it deploys your app and replaces the Lambda functions with a stub version.
  2. It also starts up a local WebSocket client and connects to the AppSync API endpoint.
  3. When a Lambda function in your app is invoked, it publishes an event, where the payload is the Lambda function request.
  4. Your local WebSocket client receives this event. It publishes an event acknowledging that it received the request.
  5. Next, it runs the local version of the function and publishes an event with the function response as the payload. The local version is run as a Node.js Worker.
  6. Finally, the stub Lambda function receives the event and responds with the payload.

Quirks

There are a couple of quirks with this setup that are worth noting.

  1. Runtime change

    The stub function that's deployed uses a different runtime than your Lambda function. You might run into this when you change the runtime in your config but the runtime of the Lambda function in the AWS Console doesn't change.

    :::tip
    The stub function that’s deployed uses a different runtime than the actual function.
    :::

    We use a different runtime because we want the function to be as fast as possible at proxying requests.

  2. Live mode persists

    If you kill the sst dev CLI, your functions are not run locally anymore but the stub function in AWS are still there. This means that it'll attempt to proxy requests to your machine and timeout.

    :::tip
    Only use sst dev in your personal stage.
    :::

    You can fix this by running sst deploy and it'll deploy the real version of your app. But the next time you run sst dev it'll need to deploy the stub back. This'll take a couple of minutes. So we recommend only using your personal stages for sst dev. And avoid flipping back and forth between dev and deploy.


Live mode

When a function is running live it sets the SST_DEV environment variable to true. So in your Node.js functions you can access it using process.env.SST_DEV.

js title="src/lambda.js" "process.env.sst_dev"
export async function main(event) {
  const body = process.env.SST_DEV ? "Hello, Live!" : "Hello, World!";

  return {
    body,
    statusCode: 200,
  };
}

This is useful if you want to access some resources locally.


Connect to a local DB

For example, when running locally you might want to connect to a local database. You can do that with the SST_DEV environment variable.

js
const dbHost = process.env.SST_DEV
  ? "localhost"
  : "amazon-string.rds.amazonaws.com";

Cost

AWS AppSync Events that powers Live is completely serverless. So you don't get charged when it's not in use.

It's also pretty cheap. It's roughly $1.00 per million messages and $0.08 per million connection minutes. You can check out the details here.

This approach has been economical even for large teams with dozens of developers.


Privacy

All the data stays between your local machine and your AWS account. There are no 3rd party services that are used.

Live also supports connecting to AWS resources inside a VPC.


Using a VPC

By default your local functions cannot connect to resources in a VPC. You can fix this by either setting up a VPN connection or creating a tunnel.


Creating a tunnel

To create a tunnel, you'll need to:

  1. Enable the bastion host in your VPC.

    ts title="sst.config.ts" "{ bastion: true }"
    new sst.aws.Vpc("MyVpc", { bastion: true });
  2. Install the tunnel.

    bash "sudo"
    sudo sst tunnel install

    This needs sudo to create the network interface on your machine. You only need to do this once.

    :::note
    For NixOS users you will also need to declare the following sudo configuration to complete the setup:

    nix
    security.sudo.extraRules = [
        {
          users = ["jay"]; # Your user
          commands = [
            {
              command = "/opt/sst/tunnel tunnel start *";
              options = ["NOPASSWD" "SETENV"];
            }
          ];
        }
      ];

    :::

  3. Run sst dev.

    bash
    sst dev

This starts the tunnel automatically; notice the Tunnel tab on the left. Now your local environment can connect to resources in your VPC.


Setting up a VPN connection

To set up a VPN connection, you'll need to:

  1. Setup a VPN connection from your local machine to your VPC network. You can use the AWS Client VPN service to set it up. Follow the Mutual authentication section in this doc to setup the certificates and import them into your Amazon Certificate Manager.
  2. Then create a Client VPC Endpoint, and associate it with your VPC.
  3. And, finally install Tunnelblick locally to establish the VPN connection.

Note that, the AWS Client VPC service is billed on an hourly basis but it's fairly inexpensive. Read more on the pricing here.


Breakpoints

Since Live runs your functions locally, you can set breakpoints and debug your functions in your favorite IDE.

For VS Code, you'll need to enable Auto Attach from the Command Palette. Hit Ctrl+Shift+P or Cmd+Shift+P, type in Debug: Toggle Auto Attach and select Always.

:::note
You need to start a new terminal in VS Code after enabling Auto Attach.
:::

Now open a new terminal in VS Code, run sst dev, set a breakpoint in a function, and invoke the function.


Changelog

Live is a feature that was created by SST when it first launched back in 2021. It's gone through a few different iterations since then.

SST Version Change
v0.5.0 Then called Live Lambda, used a API Gateway WebSocket API and a DynamoDB table.
v2.0.0 Switched to using AWS IoT, this was roughly 2-3x faster.
v3.3.1 Switched to using AWS AppSync Events, which is even faster and handles larger payloads better.

Www Src Content Docs Docs Migrate From V2 (`www/src/content/docs/docs/migrate-from-v2.mdx`)


title: Migrate From v2

import config from '../../../../config.ts';

import { Tabs, TabItem } from '@astrojs/starlight/components';

This guide will help you migrate your SST v2 apps to v3. We look at the major differences between v2 and v3 below. But to get a quick intro, we recommend reading the What is SST and Basics docs.

:::tip
We recently migrated our demo notes app from v2 to v3. You use these changes as reference.
:::

We'll then go over a migration plan that you can use. The exact details of this will be different from team to team depending on the resources in it, and sensitivity of downtime.


Getting help

SST v3 has been around for a few months with a pretty sizeable community on Discord. We've created a channel for folks looking to migrate.

Join #migrate-from-v2 on Discord.


Not supported

While the goal with v3 is to support most of what's in v2, there are a few things that haven't been supported yet. There are also a couple of them that are currently in beta and will be released in the near future.

Construct GitHub Issue
Script #811
Function non-Node.js runtimes Container, Custom

Feel free to let us know via the linked GitHub issues if these are blockers for you. It'll help us prioritize this list.


Major changes

If you are coming from SST v2, it's worth starting with the big differences between v2 and v3. It'll help you understand the types of changes you'll need to make as you migrate.


No CloudFormation

Let's start with the obvious. SST v3 moves away from CloudFormation and CDK, we've written in detail about why we decided to do this.

No CloudFormation, means a couple of things:

  1. There are no stacks, all the resources are defined through the same function in the sst.config.ts.
  2. The outputs of constructs or components are different. These used to be tokens that would get replaced on deploy. Now they are something called Outputs.
  3. The state of your app is stored locally and backed up to S3. Learn more about State.

No CDK

And moving away from CDK means:

  1. You cannot fall back to CDK constructs if something isn't supported by SST. Instead there is the AWS provider from Pulumi that's built on Terraform. There are also 150+ other providers that allow you to build on any cloud. Check out the Directory.

    If you are using a lot of higher level CDK constructs in your v2 app, it's going to be really hard to migrate to v3. The Pulumi/Terraform ecosystem is fairly complete but it's mainly made up of low level resources. You might not have ready replacements for your CDK constructs.

  2. Since the constructs or components are no longer built on CDK; they don't have a cdk prop. Instead, there's a transform prop that lets you modify the props that a component sends to its underlying resources. Learn more about the transform prop.


sst.config.ts

The sst.config.ts is similar in v3 but there are some changes. Here's a comparison of the general structure, we look at this in detail in a section below.

```ts title="sst.config.ts" export default $config({ // Your app's config app(input) { return { name: "my-sst-app", home: "aws" }; }, // Your app's resources async run() { } }); ``` ```ts title="sst.config.ts" export default { // Your app's config config(_input) { return { name: "my-sst-app", region: "us-east-1" }; }, // Your app's resources stacks(app) { } } satisfies SSTConfig; ```

Learn more about the new sst.config.ts.


sst dev

The sst dev CLI has been completely reworked. It now runs a multiplexer that deploys your app and runs your frontends together. So you don't need to:

  • Start your frontend separately
  • Need to wrap your frontend dev script with sst bind

Learn more about sst dev.


sst build

There is no sst build CLI. Instead you can run sst diff to see what changes will be deployed, without doing an actual deploy.

Learn more about sst diff.


Resource binding

Resource binding is now called resource linking, the bind prop is now renamed to link. The Node.js client or JS SDK has been reworked so that all linked resources are now available through the Resource object. We'll look at this in detail below.

The client handlers and hooks have not been supported yet.

Learn more about Resource linking.


Secrets

Secrets are not stored in SSM. Instead they are encrypted and stored in your state file. It's encrypted using a passphrase that's stored in SSM.

Loading secrets in your functions no longer needs a top-level await.


Migration plan

Say you have a v2 app in a git repo that's currently deployed to production. Here's how we recommend carrying out the migration.

  1. Use the steps below to migrate over your app to a non-prod stage. You don't need to import any resources, just recreate them.
  2. Test your non-prod version of your v3 app.
  3. Then for your prod stage, follow the steps below and make the import, domain, and subscriber changes.
  4. Once the prod version of your v3 app is running, clean up some of the v2 prod resources.

:::caution
These are recommendations and the specific details depend on the type of resources you have.
:::

The general idea here is to have the v2 app hand over control of the underlying resources to the v3 version of the app.


Setup

  1. Start by setting the removal policy to retain in the v2 app for the production stages. This ensures resources don't get accidentally removed.

    ts
    app.setDefaultRemovalPolicy("retain");

    :::caution
    You'll want to deploy your app once after setting this.
    :::

  2. Create a new branch in your repo for the upcoming changes.

  3. For the prod version of the v3 app, pick a different stage name. Say your prod stage in v2 is called production. Maybe use prod, main, or live for your v3 app. Or vice versa. This isn't strictly necessary, but we recommend doing this because you don't want to change the wrong resources by mistake.


Init v3

Now let's set up our new v3 app in the root of your project.

  1. Update SST to v3. Or set the version by hand in your package.json. Make sure to this across all the packages in your repo.

    bash frame="none"
    npm update sst

    Ensure v3 is installed.

    bash frame="none"
    npx sst version
  2. Backup the v2 config with.

    bash frame="none"
    mv sst.config.ts sst.config.ts.bk
  3. Init a v3 app.

    bash frame="none"
    npx sst init

    :::caution
    Make sure to use the same app name.
    :::

  4. Set the removal policy to retain. Similar to setDefaultRemovalPolicy in v2, you can configure the removal policy in sst.config.ts in v3.

    ts title="sst.config.ts" {4}
    app(input) {
      return {
        name: "my-sst-app",
        removal: input?.stage === "production" ? "retain" : "remove"
      };
    }

    By default, v3 has removal policy set to retain for the production stage, and remove for other stages.

  5. Deploy an empty app to ensure the app is configured correctly.

    bash frame="none"
    npx sst deploy
  6. Update the dev scripts for your frontend. Remove the sst bind from the dev script in your package.json. For example, for a Next.js app.

    diff lang="js" title="package.json"
    - "dev": "sst bind next dev",
    + "dev": "next dev",
  7. Remove any CDK related packages from your package.json.


Migrate stacks

Now before we start making changes to our constructs, you might have some stacks code in your sst.config.ts.

Take a look at the list below and apply the changes that matter to you.


Restructure

Since you don't have to import the constructs and there are no stacks, you'll need to change how your constructs are structured.

For example, in the monorepo notes app we made these changes.

```ts title="sst.config.ts" export default $config({ // ...
text
async run() {
  await import("./infra/api");
  await import("./infra/storage");
  await import("./infra/frontend");
  const auth = await import("./infra/auth");

  return {
    UserPool: auth.userPool.id,
    Region: aws.getRegionOutput().region,
    IdentityPool: auth.identityPool.id,
    UserPoolClient: auth.userPoolClient.id,
  };
}

}

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
import { SSTConfig } from "sst";
import { ApiStack } from "./stacks/ApiStack";
import { AuthStack } from "./stacks/AuthStack";
import { StorageStack } from "./stacks/StorageStack";
import { FrontendStack } from "./stacks/FrontendStack";

export default {
  // ...

  stacks(app) {
    app
      .stack(StorageStack)
      .stack(ApiStack)
      .stack(AuthStack)
      .stack(FrontendStack);
  }
} satisfies SSTConfig;

We store our infrastructure files in the infra/ directory in v3. You can refer to the demo notes app to see how these are structured.


Migrate runtime

For your runtime code, your functions and frontend; there are fairly minimal changes. The clients or the JS SDK have been reorganized.

You can make these changes now or as you are migrating each construct. Check out the steps below.


Migrate constructs

Constructs in v2 have their equivalent components in v3. Constructs fall into roughly these 3 categories:

  1. Transient β€” these don't contain data, like Function, Topic, or Queue.
  2. Data β€” these contain application data, like RDS, Table, or Bucket.
  3. Custom domains β€” these have custom domains configured, like Api, StaticSite, or NextjsSite.
  4. Subscribers β€” these are constructs that subscribe to other constructs, like the Bucket, Queue, or Table subscribers.

We'll go over each of these types and copy our v2 constructs over as v3 components.


Transient constructs

Constructs like Function, Cron, Topic, Queue, and KinesisStream do not contain data. They can be recreated in the v3 app.

Simply copy them over using the reference below.


Data constructs

Constructs like RDS, Table, Bucket, and Cognito contain data. If you do not need to keep the data, you can recreate them like what you did above. This is often the case for non-production stages.

However, for production stages, you need to import the underlying AWS resource into the v3 app.

For example, here are the steps for importing an S3 bucket named app-prod-MyBucket.

  1. Import the resource

    Say the bucket is defined in SST v2, and the bucket name is app-prod-MyBucket.

    ts title="v2"
    const bucket = new Bucket(stack, "MyBucket");

    You can use the import and transform props to import it.

    ts title="v3"
    const bucket = new sst.aws.Bucket("MyBucket", {
      transform: {
        bucket: (args, opts) => {
          args.bucket = "app-prod-MyBucket";
          opts.import = "app-prod-MyBucket";
        },
        cors: (args, opts) => {
          opts.import = "app-prod-MyBucket";
        },
        policy: (args, opts) => {
          opts.import = "app-prod-MyBucket";
        },
        publicAccessBlock: (args, opts) => {
          opts.import = "app-prod-MyBucket";
        }        
      }
    });

    Import is a process of bringing previously created resources into your SST app and allowing it to manage it moving forward. Learn more about importing resources.

  2. Deploy

    You'll get an error if the resource configurations in your code does not match the exact configuration of the bucket in your AWS account.

    This is good because we don’t want to change our old resource.

  3. Update props

    In the error message, you'll see the props you need to change. Add them to the corresponding transform block.

And deploy again.

:::caution
Make sure the v2 app is set to retain to avoid accidentally removing imported resources.
:::

After the bucket has been imported, the v2 app can still make changes to the resource. If you try to remove the v2 app or remove the bucket from the v2 app, the S3 bucket will get removed. To prevent this, ensure that had the removal policy in the v2 app to retain.


Constructs with custom domains

Constructs like the following have custom domains.

  • Frontends like StaticSite, NextjsSite, SvelteKitSite, RemixSite, AstroSite, SolidStartSite
  • APIs like Api, ApiGatewayv1, AppSyncApi, WebSocketApi
  • Service

For non-prod stages you can just recreate them.

However, if they have a custom domain, you need to deploy them in steps to avoid any downtime.

  1. First, create the resource in v3 without a custom domain. So for Nextjs for example.

    ```ts title="sst.config.ts" new sst.aws.Nextjs("MySite"); ``` ```ts title="sst.config.ts" new NextjsSite(stack, "MySite", { customDomain: "domain.com" }); ```
  2. Deploy your v3 app.

  3. When you are ready, flip the domain using the override prop.

    ts title="sst.config.ts" {4}
    new sst.aws.Nextjs("MySite", {
      domain: {
       name: "domain.com",
       dns: sst.aws.dns({ override: true })
      }
    });

    This updates the DNS record to point to your new Next.js app.

And deploy again.

:::caution
Make sure the v2 app is set to retain to avoid accidentally removing imported resources.
:::

After the DNS record has been overridden, the v2 app can still make changes to it. If you try to remove the v2 app, the record will get removed. To prevent this, ensure that the removal policy in the v2 app to retain.


Subscriber constructs

Many constructs have subscribers that help with async processing. For example, the Queue has a consumer, Bucket has the notification, and Table constructs have streams. You can recreate the constructs in your v3 app.

However recreating the subscribers for a production stage with an imported resource is not straight forward:

  • Recreating the consumer for an imported Queue will fail because a Queue can only have 1 consumer.
  • And, recreating the consumer for an imported DynamoDB Table will result in double processing. As in, an event will be processed both in your v2 and v3 app.

Here's how we recommend getting around this.

  1. Deploy the v3 app without the subscribers. Either by commenting out the .subscribe() call, or by returning early in the subscriber function.
  2. When you are ready to flip, remove the subscribers in the v2 app and deploy.
  3. Add the subscribers to the v3 app and deploy.

This ensures that the same subscriber is only attached once to a resource.


Clean up

Now that your v3 app is handling production traffic. We can optionally go clean up a few things from the v2 app.

:::tip
We recommend doing a clean up after your v3 app has been in production for a good amount of time.
:::

The resources that were recreated in v3, the ones that were not imported, can now be removed. However, since we have v2 app set to retain, this is going to be a manual process.

You can go to the CloudFormation console, look at the list of resources in your v2 app's stacks and remove them manually.

Finally, when you run sst remove for your v2 app, it'll remove the CloudFormation stacks as well.


CI/CD

You probably have git push to deploy or CI/CD set up for your apps. If you are using GitHub Actions; there shouldn't be much of a difference between v2 and v3.

If you are using Seed to deploy your v2 app; then you'll want to migrate to using Autodeploy on the SST Console. We are currently not planning to support v3 on Seed.

There are a couple of key reasons to Autodeploy through the Console:

  • The builds are run in your AWS account.
  • You can configure your workflow through your sst.config.ts.
  • And you can see which resources were updated as a part of the deploy.

To enable Autodeploy on the Console, you'll need to:

  1. Create a new account on the Console β€” console.sst.dev
  2. Link your AWS account
  3. Connect your repo
  4. Configure your environments
  5. And git push

Learn more about Console and Autodeploy.


sst.config.ts

Listed below are some of the changes to your sst.config.ts in general.


No imports

All the constructs or components are available in the global context. So there's no need to import anything. Your app's package.json only needs the sst package. There are no extra CDK or infrastructure related packages.


Globals

There are a couple of global variables, $app and $dev that replace the app argument that's passed into the stacks() method of your sst.config.ts.

  1. $app.name gives you the name of app. Used to be app.name.
  2. $app.stage gives you the name of stage. Used to be app.stage.
  3. $dev === true tells you if you are in dev mode. Used to be app.mode === "dev.
  4. $dev === false tells you if it's being deployed. Used to be app.mode === "deploy.
  5. There is no app.mode === remove replacement since your components are not evaluated on sst remove.
  6. There is no app.region since in v3 you can deploy resources to different regions or AWS profiles or providers. To get the default AWS provider you can use aws.getRegionOutput().region.

No stacks

Also since there are no stacks. You don't have access to the stack argument inside your stack function. And no stack.addOutputs({}) method.

You can still group your constructs or components in files. But to output something you return in the run method of your config.

ts title="sst.config.ts"
async run() {
  const auth = await import("./infra/auth");

  return {
    UserPool: auth.userPool.id,
    IdentityPool: auth.identityPool.id,
    UserPoolClient: auth.userPoolClient.id
  };
}

Defaults

The set of methods that applied defaults to all the functions in your app like; addDefaultFunctionBinding, addDefaultFunctionEnv, addDefaultFunctionPermissions, and setDefaultFunctionProps can be replaced with the global $transform.

ts title="sst.config.ts"
$transform(sst.aws.Function, (args, opts) => {
  // Set the default if it's not set by the component
  if (args.runtime === undefined) {
    args.runtime = "nodejs18.x";
  }
})

Learn more about $transform.


Clients

The Node.js client, now called the JS SDK has a couple of minor changes.

Update sst to the latest version in your package.json. If you have a monorepo, make sure to update sst in all your packages.


Bind

In SST v3, you access all bound or linked resources through the Resource module.

Say you link a bucket to a function.

```ts title="sst.config.ts" {5} const bucket = new sst.aws.Bucket("MyBucket");

new sst.aws.Function("MyFunction", {
handler: "src/lambda.handler",
link: [bucket]
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts" {5}
const bucket = new Bucket(stack, "MyBucket");

new Function(stack, "MyFunction", {
  handler: "src/lambda.handler",
  bind: [bucket]
});

In your function you would access it like so.

```ts title="src/lambda.ts" "Resource.MyBucket.name" "sst" import { Resource } from "sst";

console.log(Resource.MyBucket.name);

text
</TabItem>
<TabItem label="v2">
```ts title="src/lambda.ts" "Bucket.MyBucket.bucketName" "sst/node/bucket"
import { Bucket } from "sst/node/bucket";

console.log(Bucket.MyBucket.bucketName);

Config

The same applies to Config as well. Let's look at a secret.

```ts title="sst.config.ts" {5} const secret = new sst.Secret("MySecret");

new sst.aws.Function("MyFunction", {
handler: "src/lambda.handler",
link: [secret]
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts" {5}
const secret = new Config.Secret(stack, "MySecret");

new Function(stack, "MyFunction", {
  handler: "src/lambda.handler",
  bind: [secret]
});

And in your function you access it in the same way.

```ts title="src/lambda.ts" "Resource.MySecret.value" "sst" import { Resource } from "sst";

console.log(Resource.MySecret.value);

text
</TabItem>
<TabItem label="v2">
```ts title="src/lambda.ts" "Config.MySecret" "sst/node/config"
import { Config } from "sst/node/config";

console.log(Config.MySecret);

Handlers

In v2, some modules in the Node client had handlers and hooks.

ts title="v2"
import { ApiHandler } from "sst/node/api";

export const handler = ApiHandler((event) => { });

These were experimental and are not currently supported in v3. To continue using them you can import them by first adding it to your package.json.

diff lang="json" title="package.json"
{
+  "sstv2": "npm:sst@^2",
  "sst": "^3"
}

This means that you have both v2 and v3 installed in your project. Since, they both have an sst binary, you want to make sure v3 takes precedence. So v3 should be listed after v2.

:::caution
Make sure v3 is listed after v2 in your package.json.
:::

And then import them via the sstv2 alias.

ts title="v3"
import { ApiHandler } from "sstv2/node/api";

export const handler = ApiHandler((event) => { });

Constructs

Below shows the v3 component version of a v2 construct.


Api

```ts title="sst.config.ts" const api = new sst.aws.ApiGatewayV2("MyApi", { domain: "api.example.com" });

api.route("GET /", "src/get.handler");
api.route("POST /", "src/post.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const api = new Api(stack, "MyApi", {
  customDomain: "api.example.com"
});

api.addRoutes(stack, {
  "GET /": "src/get.main",
  "POST /": "src/post.handler"
});

Job

The Task component that replaces Job is based on AWS Fargate. It runs a container task in the background.

```ts title="sst.config.ts" const cluster = new sst.aws.Cluster("MyCluster", { vpc });

new sst.aws.Task("MyTask", {
cluster,
image: {
context: "./src",
dockerfile: "Dockerfile"
}
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
new Job(stack, "MyJob", {
  handler: "src/job.main"
});

There are some key differences between Job and Task.

  1. Task is based on AWS Fargate. Job used a combination of AWS CodeBuild and Lambda.
  2. Since Task is natively based on Fargate, you can use the AWS SDK to interact with it, even in runtimes the SST SDK doesn't support.
  3. In dev mode, Task uses Fargate only, whereas Job used Lambda.
  4. While CodeBuild is billed per minute, Fargate is a lot cheaper than CodeBuild. Roughly $0.02 per hour vs $0.3 per hour on X86 machines.

Learn more about Task.


RDS

```ts title="sst.config.ts" const vpc = new sst.aws.Vpc("MyVpc"); new sst.aws.Aurora("MyDatabase", { vpc, engine: "postgres", version: "15.5", databaseName: "acme" }); ``` ```ts title="sst.config.ts" new RDS(stack, "MyDatabase", { engine: "postgresql15.5", defaultDatabaseName: "acme", migrations: "path/to/migration/scripts" }); ```

The Aurora component uses Amazon Aurora Serverless v2.

For migrations, we recommend using Drizzle Kit. Check out our Drizzle example.


Cron

```ts title="sst.config.ts" new sst.aws.Cron("MyCronJob", { schedule: "rate(1 minute)", function: "src/cron.handler" }); ``` ```ts title="sst.config.ts" new Cron(stack, "MyCronJob", { schedule: "rate(1 minute)", job: "src/cron.handler" }); ```

Table

```ts title="sst.config.ts" const table = new sst.aws.Dynamo("MyTable", { fields: { id: "string" }, primaryIndex: { hashKey: "id" } });

table.subscribe("MySubscriber", "src/subscriber.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const table = new Table(stack, "MyTable", {
  fields: {
    id: "string"
  },
  primaryIndex: { partitionKey: "id" }
});

table.addConsumers(stack, {
  consumer: "src/subscriber.handler"
});

Topic

```ts title="sst.config.ts" const topic = new sst.aws.SnsTopic("MyTopic");

topic.subscribe("MySubscriber", "src/subscriber.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const topic = new Topic(stack, "MyTopic");

topic.addSubscribers(stack, {
  subscriber: "src/subscriber.handler"
});

Queue

```ts title="sst.config.ts" const queue = new sst.aws.Queue("MyQueue");

queue.subscribe("src/subscriber.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const queue = new Queue(stack, "MyQueue");

queue.addConsumer(stack, "src/subscriber.handler");

Config

The Config construct is now broken into a Secret component and v3 has a separate way to bind any value or parameter.


Secret

```ts title="sst.config.ts" const secret = new sst.Secret("MySecret"); ``` ```ts title="sst.config.ts" const secret = new Config.Secret(stack, "MySecret"); ```

There's also a slight change to the CLI for setting secrets.

```bash "secret" npx sst secret set MySecret sk_test_abc123 ``` ```bash "secrets" npx sst secrets set MySecret sk_test_abc123 ```

Parameter

The Linkable component lets you bind or link any value.

```ts title="sst.config.ts" const secret = new sst.Linkable("MyParameter", { properties: { version: "1.2.0" } }); ``` ```ts title="sst.config.ts" const secret = new Config.Parameter(stack, "MyParameter", { value: "1.2.0" }); ```

In your function you'd access this using.

```ts title="src/lambda.ts" import { Resource } from "sst";

console.log(Resource.MyParameter.version);

text
</TabItem>
<TabItem label="v2">
```ts title="src/lambda.ts"
import { Config } from "sst/node/config";

console.log(Config.MyParameter);

Bucket

```ts title="sst.config.ts" const bucket = new sst.aws.Bucket("MyBucket");

bucket.subscribe("src/subscriber.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const bucket = new Bucket(stack, "MyBucket");

bucket.addNotifications(stack, {
  notification: "src/notification.main"
});

Service

```ts title="sst.config.ts" const cluster = new sst.aws.Cluster("MyCluster", { vpc: { id: "vpc-0d19d2b8ca2b268a1", securityGroups: ["sg-0399348378a4c256c"], publicSubnets: ["subnet-0b6a2b73896dc8c4c", "subnet-021389ebee680c2f0"], privateSubnets: ["subnet-0db7376a7ad4db5fd ", "subnet-06fc7ee8319b2c0ce"] } });

new sst.aws.Service("MyService", {
cluster,
loadBalancer: {
domain: "my-app.com",
ports: [
{ listen: "80/http" }
]
}
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
new Service(stack, "MyService", {
  customDomain: "my-app.com",
  path: "./service",
  port: 80,
  cdk: {
    vpc: Vpc.fromLookup(stack, "VPC", {
      vpcId: "vpc-0d19d2b8ca2b268a1"
    })
  }
});

Cognito

```ts title="sst.config.ts" const userPool = new sst.aws.CognitoUserPool("MyUserPool");

const client = userPool.addClient("MyClient");

new sst.aws.CognitoIdentityPool("MyIdentityPool", {
userPools: [{
userPool: userPool.id,
client: client.id
}]
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
new Cognito(stack, "MyAuth");

Function

```ts title="sst.config.ts" new sst.aws.Function("MyFunction", { handler: "src/lambda.handler" }); ``` ```ts title="sst.config.ts" new Function(stack, "MyFunction", { handler: "src/lambda.handler" }); ```

AstroSite

```ts title="sst.config.ts" new sst.aws.Astro("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new AstroSite(stack, "MyWeb", { customDomain: "my-app.com" }); ```

EventBus

```ts title="sst.config.ts" const bus = new sst.aws.Bus("Bus");

bus.subscribe("MySubscriber1", "src/function1.handler", {
pattern: {
source: ["myevent"]
}
});
bus.subscribe("MySubscriber2", "src/function2.handler", {
pattern: {
source: ["myevent"]
}
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
new EventBus(stack, "Bus", {
  rules: {
    myRule: {
      pattern: { source: ["myevent"] },
      targets: {
        myTarget1: "src/function1.handler",
        myTarget2: "src/function2.handler"
      }
    }
  }
});

StaticSite

```ts title="sst.config.ts" new sst.aws.StaticSite("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new StaticSite(stack, "MyWeb", { customDomain: "my-app.com" }); ```

RemixSite

```ts title="sst.config.ts" new sst.aws.Remix("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new RemixSite(stack, "MyWeb", { customDomain: "my-app.com" }); ```

NextjsSite

```ts title="sst.config.ts" new sst.aws.Nextjs("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new NextjsSite(stack, "MyWeb", { customDomain: "my-app.com" }); ```

AppSyncApi

```ts title="sst.config.ts" const api = new sst.aws.AppSync("MyApi", { schema: "schema.graphql", domain: "api.domain.com" });

const lambdaDS = api.addDataSource({
name: "lambdaDS",
lambda: "src/lambda.handler"
});
api.addResolver("Query user", {
dataSource: lambdaDS.name
});

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const api = new AppSyncApi(stack, "MyApi", {
  schema: "graphql/schema.graphql",
  customDomain: "api.example.com"
});

api.addDataSources(stack, {
  lambdaDS: "src/lambda.handler"
});
api.addResolvers(stack, {
  "Query user": "lambdaDS"
});

SvelteKitSite

```ts title="sst.config.ts" new sst.aws.SvelteKit("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new SvelteKitSite(stack, "MyWeb", { customDomain: "my-app.com" }); ```

SolidStartSite

```ts title="sst.config.ts" new sst.aws.SolidStart("MyWeb", { domain: "my-app.com" }); ``` ```ts title="sst.config.ts" new SolidStartSite(stack, "MyWeb", { customDomain: "my-app.com" }); ```

WebSocketApi

```ts title="sst.config.ts" const api = new sst.aws.ApiGatewayWebSocket("MyApi", { domain: "api.example.com" });

api.route("$connect", "src/connect.handler");
api.route("$disconnect", "src/disconnect.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const api = new WebSocketApi(stack, "MyApi", {
  customDomain: "api.example.com"
});

api.addRoutes(stack, {
  $connect: "src/connect.handler",
  $disconnect: "src/disconnect.handler"
});

KinesisStream

```ts title="sst.config.ts" const stream = new sst.aws.KinesisStream("MyStream");

stream.subscribe("MySubscriber", "src/subscriber.handler");

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const stream = new KinesisStream(stack, "MyStream");

stream.addConsumers(stack, {
  consumer: "src/subscriber.handler"
});

ApiGatewayV1Api

```ts title="sst.config.ts" const api = new sst.aws.ApiGatewayV1("MyApi", { domain: "api.example.com" });

api.route("GET /", "src/get.handler");
api.route("POST /", "src/post.handler");
api.deploy();

text
</TabItem>
<TabItem label="v2">
```ts title="sst.config.ts"
const api = new ApiGatewayV1Api(stack, "MyApi", {
  customDomain: "api.example.com"
});

api.addRoutes(stack, {
  "GET /": "src/get.handler",
  "POST /": "src/post.handler"
});

Congrats on getting through the migration.

If you find any errors or if you'd like to add some details to this guide, feel free to Edit this page and submit a PR.


Www Src Content Docs Docs Migrate From V3 (`www/src/content/docs/docs/migrate-from-v3.mdx`)


title: Migrate From v3

import TestimonialWall from "../../../components/TestimonialWall.astro";

SST v4 upgrades the underlying Pulumi AWS provider from v6 to v7. This guide covers migrating your SST v3 apps to v4.

:::tip
SST components are already updated for v7. Most users only need to run sst refresh and deploy.
:::

For the full list of upstream changes, refer to the Pulumi AWS v7 migration guide.


Breaking changes

No code changes are needed if you are only using SST components without transforms or direct @pulumi/aws usage. Otherwise, refer to the Pulumi AWS v7 migration guide for the full list of breaking changes.

The internal changes to SST components were minimal: mostly S3 resource renames (dropping the V2 suffix) and switching from tags to tagsAll. You can see the full list of changes in the upgrade PR.


Migration steps

When you update SST and the AWS provider version changes, sst deploy will be blocked until you migrate your state:

  1. Install the latest v4

    Make sure you're on the latest v4 version before running any commands.

  2. Update your config

    If you have any breaking changes from above, update your sst.config.ts accordingly.

  3. Review changes

    Run sst diff to preview what will change. This is a one-way migration, so it's worth reviewing first.

    bash frame="none"
    sst diff
  4. Migrate state

    Run sst refresh to migrate your state.

    :::caution
    Do not use the --target flag during migration. The state refresh must cover all resources to ensure a consistent migration.
    :::

    bash frame="none"
    sst refresh

    Repeat for each stage.

    bash frame="none"
    sst refresh --stage production

    If the stage was deployed using sst dev, use the --dev flag.

    bash frame="none"
    sst refresh --dev

    If you share resources across stages, run sst refresh on the stage where the resource is created first β€” not the stage that references it via .get().

  5. Deploy

    Once refreshed, deploy as usual.

    bash frame="none"
    sst deploy

That's it β€” once refreshed and deployed, your app is fully migrated to v4.


Upgrade testimonials

Upgrading your infra framework can feel scary. Here's what teams have shared in Discord after migrating:

<TestimonialWall testimonials={[
{
name: "waterbear",
avatar: "/testimonials/waterbear.webp",
text: "Successfully migrated two projects to SST v4.",
url: "https://discord.com/channels/983865673656705025/985222825893822485/1484243262058397829"
},
{
name: "Zac",
avatar: "/testimonials/zac.webp",
text: "We just had a succesful upgrade too! Thanks team.",
url: "https://discord.com/channels/983865673656705025/1481358917106929786/1481383395618459771"
},
{
name: "Rodrigo",
avatar: "/testimonials/rodrigo.webp",
text: "I just migrated our production app to SST v4. It's a fairly complex app with frontends, REST APIs, plenty of backend processes, usage of S3 buckets, DynamoDB, RDS, etc.\n\nEverything just worked fine.",
url: "https://discord.com/channels/983865673656705025/983865673656705028/1481358917106929786"
},
{
name: "sockthedev",
avatar: "/testimonials/sockthedev.webp",
text: "Excellent work on the upgrade to v4! I did a large project upgrade with no problems.",
url: "https://discord.com/channels/983865673656705025/983865673656705028/1476430580173508800"
},
{
name: "marv",
avatar: "/testimonials/marv.webp",
text: "We flawlessly upgraded our production environment to v4 (ECS, RDS, SQS, etc.)",
// text: "We flawlessly upgraded our production environment to v4 (ECS, RDS, SQS, etc.)\n\nSST v4 is the best thing that ever happened since v3 happened to v2.",
url: "https://discord.com/channels/983865673656705025/983865673656705028/1489243087166378197"
}
]} />


Www Src Content Docs Docs Planetscale (`www/src/content/docs/docs/planetscale.mdx`)


title: PlanetScale

PlanetScale is a cloud database platform built for speed and scalability. This guide covers how to set it up with SST.

Install

Add the PlanetScale provider to your SST app. Learn more about providers.

bash
sst add planetscale

This adds the provider to your sst.config.ts.

ts title="sst.config.ts" {3}
{
  providers: {
    planetscale: "1.0.0",
  },
}

Then set the PLANETSCALE_SERVICE_TOKEN and PLANETSCALE_SERVICE_TOKEN_ID environment variables. You can create a service token in the PlanetScale dashboard under Settings > Service tokens. The token needs at least the following permissions for the upcoming examples:

  • connect_branch
  • connect_production_branch
  • create_branch
  • delete_branch
  • delete_branch_password
  • read_branch
  • read_database

Reference a database

Reference an existing PlanetScale Vitess database directly from your SST config.

ts title="sst.config.ts"
const db = planetscale.getDatabaseVitessOutput({
  id: "mydb",
  organization: "myorg",
});

:::note
This guide uses the Vitess resources. PlanetScale also supports Postgres with equivalent resources and functions: PostgresBranch, PostgresBranchRole, getDatabasePostgresOutput, and getPostgresBranchOutput.
:::


Branch per stage

PlanetScale supports database branching natively. Combined with SST, every stage and every pull request can get its own isolated database branch automatically.

Conditionally create or reference a branch based on the current stage.

ts title="sst.config.ts"
const branch =
  $app.stage === "production"
    ? planetscale.getVitessBranchOutput({
        id: db.defaultBranch,
        organization: db.organization,
        database: db.name,
      })
    : new planetscale.VitessBranch("DatabaseBranch", {
        database: db.name,
        organization: db.organization,
        name: $app.stage,
        parentBranch: db.defaultBranch,
      });

In production, it references the existing default branch. In any other stage, it creates a new branch from it. So sst deploy --stage pr-42 creates a pr-42 branch in PlanetScale.

:::tip
Configure Autodeploy in the SST Console to do this automatically on every pull request.
:::

This is a pattern used in production by terminal.shop and OpenCode.


Create a password for the branch and wrap it in a sst.Linkable.

ts title="sst.config.ts"
const password = new planetscale.VitessBranchPassword("DatabasePassword", {
  database: db.name,
  organization: db.organization,
  branch: branch.name,
  role: "admin",
  name: `${$app.name}-${$app.stage}`,
});

export const database = new sst.Linkable("Database", {
  properties: {
    host: password.accessHostUrl,
    username: password.username,
    password: password.plainText,
    database: db.name,
    port: 3306,
  },
});

The Linkable component lets you wrap arbitrary values and make them available to any function or service you link it to. Learn more about linking resources.


Connect to the database

Link the database to any function or service.

ts title="sst.config.ts"
new sst.aws.Function("Api", {
  handler: "src/api.handler",
  link: [database],
});

Then access the credentials in a type-safe way through Resource. For example, with Drizzle ORM.

ts title="src/drizzle.ts"
import { drizzle } from "drizzle-orm/planetscale-serverless";
import { Resource } from "sst";

export const db = drizzle({
  connection: {
    host: Resource.Database.host,
    username: Resource.Database.username,
    password: Resource.Database.password,
  },
});

You can use any other ORM or database driver the same way.


Examples

Check out the full examples:


Www Src Content Docs Docs Policy Packs (`www/src/content/docs/docs/policy-packs.mdx`)


title: Policy Packs

Policy packs let you enforce rules on your infrastructure before deploying. They use Pulumi Policy Packs under the hood, and work with the sst deploy, sst diff and sst dev commands.


Quick start

Say you want to require permission boundaries on all IAM roles. Start by creating a policy pack.

bash frame="none"
mkdir policy-pack && cd policy-pack
pulumi policy new aws-typescript

This creates a PulumiPolicy.yaml, index.ts, and package.json. Update the index.ts with your policy.

ts title="policy-pack/index.ts"
import * as aws from "@pulumi/aws";
import { PolicyPack, validateResourceOfType } from "@pulumi/policy";

new PolicyPack("aws-policies", {
  policies: [
    {
      name: "iam-role-requires-permission-boundary",
      description: "IAM roles must have a permission boundary.",
      enforcementLevel: "mandatory",
      validateResource: validateResourceOfType(
        aws.iam.Role,
        (role, _args, reportViolation) => {
          if (!role.permissionsBoundary) {
            reportViolation(
              "IAM roles must have a permission boundary."
            );
          }
        }
      ),
    },
  ],
});

Then deploy with the --policy flag.

bash frame="none"
sst deploy --policy ./policy-pack

If any resource violates a mandatory policy, the deploy is blocked.


Enforcement levels

Each policy has an enforcementLevel that controls what happens when a resource violates it.

  • mandatory β€” blocks the deploy. The resource must be fixed before it can be deployed.
  • advisory β€” prints a warning but allows the deploy to continue.
ts title="policy-pack/index.ts"
{
  name: "no-wildcard-resources",
  description: "Avoid wildcard resources in IAM policies.",
  enforcementLevel: "advisory",
  validateResource: validateResourceOfType(
    aws.iam.RolePolicy,
    (policy, _args, reportViolation) => {
      // ...
    }
  ),
}

Writing a policy pack

A policy pack is a directory with three files:

  • PulumiPolicy.yaml β€” metadata and runtime config
  • index.ts β€” your policies
  • package.json β€” dependencies

The PulumiPolicy.yaml looks like this.

yaml title="policy-pack/pulumipolicy.yaml"
description: A minimal Policy Pack for AWS using TypeScript.
runtime: nodejs
main: dist/index.js

And the package.json needs the @pulumi/policy package, plus any provider packages your policies check against.

json title="policy-pack/package.json"
{
  "dependencies": {
    "@pulumi/aws": "^6.0.0",
    "@pulumi/policy": "^1.20.0"
  }
}

You can check the full example on GitHub and the Pulumi Policy Pack docs for more details.


Www Src Content Docs Docs Providers (`www/src/content/docs/docs/providers.mdx`)


title: Providers

import VideoAside from "../../../components/VideoAside.astro";

A provider is what allows SST to interact with the APIs of various cloud services. These are packages that can be installed through your sst.config.ts.

SST is built on Pulumi/Terraform and supports 150+ providers. This includes the major clouds like AWS, Azure, and GCP; but also services like Cloudflare, Stripe, Vercel, Auth0, etc.

Check out the full list in the Directory.


Install

To add a provider to your app run.

bash
sst add <provider>

This command adds the provider to your config, installs the packages, and adds the namespace of the provider to your globals.

:::caution
Do not import the provider packages in your sst.config.ts.
:::

SST manages these packages internally and you do not need to import the package in your sst.config.ts.

:::tip
Your app can have multiple providers.
:::

The name of a provider comes from the name of the package in the Directory. For example, sst add planetscale, will add the following to your sst.config.ts.

ts title="sst.config.ts"
{
  providers: {
    planetscale: "0.0.7"
  }
}

You can add multiple providers to your app.

ts title="sst.config.ts"
{
  providers: {
    aws: "6.27.0",
    cloudflare: "5.37.1"
  }
}

Read more about the sst add command.


Configure

You can configure a provider in your sst.config.ts. For example, to change the region for AWS.

ts title="sst.config.ts"
{
  providers: {
    aws: {
      region: "us-west-2"
    }
  }
}

You can check out the available list of options that you can configure for a provider over on the provider's docs. For example, here are the ones for AWS and Cloudflare.


Versions

By default, SST installs the latest version. If you want to use a specific version, you can change it in your config.

ts title="sst.config.ts"
{
  providers: {
    aws: {
      version: "6.27.0"
    }
  }
}

If you make any changes to the providers in your config, you'll need to run sst install.

:::tip
You'll need to run sst install if you update the providers in your config.
:::

The version of the provider is always pinned to what's in the sst.config.ts and does not auto-update. This is the case, even if there is no version set. This is to make sure that the providers don't update in the middle of your dev workflow.

:::note
Providers don't auto-update. They stick to the version that was installed initially.
:::

So if you want to update it, you'll need to change it manually and run sst install.


Credentials

Most providers will read your credentials from the environment. For example, for Cloudflare you might set your token like so.

bash
export CLOUDFLARE_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
export CLOUDFLARE_DEFAULT_ACCOUNT_ID=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa

Follow the Cloudflare guide for the recommended setup.

However, some providers also allow you to pass in the credentials through the config.

ts title="sst.config.ts"
{
  providers: {
    cloudflare: {
      apiToken: "aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa"
    }
  }
}

Read more about configuring providers.


Components

The provider packages come with components that you can use in your app.

For example, running sst add aws will allow you to use all the components under the aws namespace.

ts title="sst.config.ts"
new aws.s3.BucketV2("b", {
  bucket: "mybucket",
  tags: {
    Name: "My bucket"
  }
});

Aside from components in the providers, SST also has a list of built-in components. These are typically higher level components that make it easy to add features to your app.

You can check these out in the sidebar. Read more about Components.


Functions

Aside from the components, there are a collection of functions that are exposed by a provider. These are listed in the Pulumi docs as getXXXXXX on the sidebar.

For example, to get the AWS account being used in your app.

ts title="sst.config.ts"
const current = await aws.getCallerIdentity({});

const accountId = current.accountId;
const callerArn = current.arn;
const callerUser = current.userId;

Or to get the current region.

ts title="sst.config.ts"
const current = await aws.getRegion({});

const region = current.name;

Output versions

The above are async methods that return promises. That means that if you call these in your app, they'll block the deployment of any resources that are defined after it.

:::tip
Outputs don't block your deployments.
:::

So we instead recommend using the Output version of these functions. For example, if we wanted to set the above as environment variables in a function, we would do something like this

ts title="sst.config.ts"
new sst.aws.Function("MyFunction, {
  handler: "src/lambda.handler",
  environment: {
    ACCOUNT: aws.getCallerIdentityOutput({}).accountId,
    REGION: aws.getRegionOutput().region
  }
}

The aws.getXXXXOutput functions typically return an object of type Output. Read more about Outputs.


Instances

You can create multiple instances of a provider that's in your config. By default SST creates one instance of each provider in your sst.config.ts with the defaults. By you can create multiple instances in your app.

ts title="sst.config.ts"
const useast1 = new aws.Provider("AnotherAWS");

This is useful for multi-region or multi-account deployments.


Multi-region

You might want to create multiple providers in cases where some resources in your app need to go to one region, while others need to go to a separate region.

Let's look at an example. Assume your app is normally deployed to us-west-1. But you need to create an ACM certificate that needs to be deployed to us-east-1.

ts {1} title="sst.config.ts" "{ provider: useast1 }"
const useast1 = new aws.Provider("useast1", { region: "us-east-1" });

new sst.aws.Function("MyFunction, "src/lambda.handler");

new aws.acm.Certificate("cert", {
  domainName: "foo.com",
  validationMethod: "EMAIL",
}, { provider: useast1 });

Here the function is created in your default region, us-west-1. While the certificate is created in us-east-1.


Www Src Content Docs Docs Reference Resources (`www/src/content/docs/docs/reference-resources.mdx`)


title: Reference Resources

Referencing is the process of using some externally created resources in your SST app; without having SST manage them.

This is for when you have some resources that are either managed by a different team or a different IaC tool. Typically these are low-level resources and not SST's built-in components.

There are a few different ways this shows up in SST.


Lookup a resource

Let's say you have an existing resource that you want to use in your SST app.

You can look it up by passing in a property of the resource. For example, to look up a previously created S3 Bucket with the following name.

txt
mybucket-xnbmhcvd

We can use the static aws.s3.BucketV2.get method.

ts title="sst.config.ts"
const bucket = aws.s3.BucketV2.get("MyBucket", "mybucket-xnbmhcvd");

This gives you the same bucket object that you'd get if you had created this resource in your app.

Here we are assuming the bucket wasn't created through an SST app. This is why we are using the low-level aws.s3.BucketV2. If this was created in an SST app or in another stage in the same app, there's a similar static sst.aws.Bucket.get method. Learn more about sharing across stages.


How it works

When you create a resource in your SST app, two things happen. First, the resource is created by making a bunch of calls to your cloud provider. Second, SST makes a call to get the resource from the cloud provider. The data that it gets back is stored in your state.

:::note
When you lookup a resource or create it, you get the same type of object.
:::

When you lookup a resource, it skips the creation step and just gets the resource. It does this every time you deploy. But the object you get in both cases is the same.


Lookup properties

The properties used to do a lookup are the same ones that you'd use if you were trying to import them.

:::tip
You can look up a resource with its Import Property.
:::

We've compiled a list of the most commonly lookedup low-level resources and their Import Properties.

Most low-level resources come with a static get method that use this property to look up the resource.


Make it linkable

Let's take it a step further.

You can use the sst.Linkable component, to be able to link any property of this resource.

ts title="sst.config.ts" {3}
const storage = new sst.Linkable("MyStorage", {
  properties: {
    domain: bucket.bucketDomainName
  }
});

Here we are using the domain name of the bucket as an example.


And link it to a function.

ts title="sst.config.ts" {9}
new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler",
  link: [storage]
});

Now you can use the SDK to access them at runtime.

js title="src/lambda.ts"
import { Resource } from "sst";

console.log(Resource.MyStorage.domain);

Pass in a resource

Aside from looking up resources, you can also pass existing resources in to the built-in SST components. This is typically when you are trying to create a new resource and it takes another resource as a part of it.

For example, let's say you want to add a previously created function as a subscriber to a queue. You can do so by passing its ARN.

ts title="sst.config.ts" {3}
const queue = new sst.aws.Queue("MyQueue");

queue.subscribe("arn:aws:lambda:us-east-1:123456789012:function:my-function");

How it works

SST is simply asking for the props the underlying resource needs. In this case, it only needs the function ARN.

However, for more complicated resources like VPCs, you might have to pass in a lot more. Here we are creating a new function in an existing VPC.

ts title="sst.config.ts"
new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler",
  vpc: {
    subnets: ["subnet-0be8fa4de860618bb"],
    securityGroups: ["sg-0be8fa4de860618bb"]
  }
});

Whereas, for the Cluster component, you might need to pass in a lot more.

ts title="sst.config.ts"
new sst.aws.Cluster("MyCluster", {
  vpc: {
    id: "vpc-0be8fa4de860618bb",
    securityGroups: ["sg-0be8fa4de860618bb"],
    containerSubnets: ["subnet-0be8fa4de860618bb"],
    loadBalancerSubnets: ["subnet-8be8fa4de850618ff"]
  }
});

These are listed under the vpc prop of the Cluster component.


Attach to a resource

Referencing existing resources also comes in handy when you are attaching to an existing resource. For example, to add a subscriber to an externally created queue:

ts title="sst.config.ts" {3}
sst.aws.Queue.subscribe("arn:aws:sqs:us-east-1:123456789012:MyQueue", "src/subscriber.handler");

Here we are using the static subscribe method of the Queue component. And it takes the queue ARN that you are trying to attach to.

There are a few other built-in SST components that have static methods like this.

  • Bus
  • Dynamo
  • SnsTopic
  • KinesisStream

With this you can continue to manage the root resource outside of SST, while still being able to attach to them.


Www Src Content Docs Docs Set Up A Monorepo (`www/src/content/docs/docs/set-up-a-monorepo.mdx`)


title: Set up a Monorepo

While, drop-in mode is great for simple projects, we recommend using a monorepo for projects that are going to have multiple packages.

:::tip
We created a monorepo template for your SST projects.
:::

However, setting up a monorepo with everything you need can be surprisingly tricky. To fix this we created a template for a TypeScript monorepo that uses npm workspaces.


How to use

To use this template.

  1. Head over to github.com/sst/monorepo-template

  2. Click on Use this template and create a new repo.

  3. Clone the repo.

  4. From the project root, run the following to rename it to your app.

    bash
    npx replace-in-file "/monorepo-template/g" "MY_APP" "./**/*" --verbose
  5. Install the dependencies.

    bash
    npm install

Now just run npx sst dev from the project root.


Project structure

The app is split into the separate packages/ and an infra/ directory.

txt {2}
my-sst-app
β”œβ”€ sst.config.ts
β”œβ”€ package.json
β”œβ”€ packages
β”‚  β”œβ”€ functions
β”‚  β”œβ”€ scripts
β”‚  └─ core
└─ infra

The packages/ directory has your workspaces and this is in the root package.json.

json title="package.json
"workspaces": [
  "packages/*"
]

Let's look at it in detail.


Packages

The packages/ directory includes the following:

  • core/

    This directory includes shared code that can be used by other packages. These are
    defined as modules. For example, we have an Example module.

    ts title="packages/core/src/example/index.ts"
    export namespace Example {
      export function hello() {
        return "Hello, world!";
      }
    }

    We export this using the following in the package.json:

    json title="packages/core/package.json"
    "exports": {
      "./*": [
        "./src/*\/index.ts",
        "./src/*.ts"
      ]
    }

    This will allow us to import the Example module by doing:

    ts
    import { Example } from "@monorepo-template/core/example";
    
    Example.hello();

    We recommend creating new modules for the various domains in your project. This roughly follows Domain Driven Design.

    We also have Vitest configured for testing this package with the sst shell CLI.

    bash
    npm test
  • functions/

    This directory includes our Lambda functions. It imports from the core/
    package by using it as a local dependency.

  • scripts/

    This directory includes scripts that you can run on your SST app using the sst shell CLI
    and tsx. For example, to the run the example
    scripts/src/example.ts, run the following from packages/scripts/.

    bash
    npm run shell src/example.ts

You can add additional packages to the packages/ directory. For example, you might add a frontend/ and a backend/ package.


Infrastructure

The infra/ directory allows you to logically split the infrastructure of your app into separate files. This can be helpful as your app grows.

In the template, we have an api.ts, and storage.ts. These export resources that can be used in the other infrastructure files.

ts title="infra/storage.ts"
export const bucket = new sst.aws.Bucket("MyBucket");

We then dynamically import them in the sst.config.ts.

ts title="sst.config.ts"
async run() {
  const storage = await import("./infra/storage");
  await import("./infra/api");

  return {
    MyBucket: storage.bucket.name
  };
}

Finally, some of the outputs of our components are set as outputs for our app.


Www Src Content Docs Docs Share Across Stages (`www/src/content/docs/docs/share-across-stages.mdx`)


title: Share Across Stages

You define all the resources in your app in your sst.config.ts. These resources then get created for each stage that you deploy to.

However, there might be some cases where you don't want to recreate certain resources for every stage.


Why share

You typically want to share for cases where:

  • Resources that are expensive and their pricing is not truly pay-per-use, like your Postgres cluster.
  • Or, if they contain data that these new stages need to reuse. For example, your PR stages might just be for testing against your staging data and don't need to recreate some resources.

While it might be tempting to share more resources across stages, we only recommend doing it for the above cases.


How to share

To help with this some SST components come with a static get method. These components are typically ones that people want to be able to share. Here are some components that have this:

If you'd like us to add to this list, feel free to open a GitHub issue.

It's worth noting that complex components like our frontends, Nextjs, or StaticSite, are not likely to be supported. Both because they are made up of a large number of resources. But also because they really aren't worth sharing across stages.

Let's look at an example.


Example

The static get in the Bucket component has the following signature. It takes the name of the component and the name of the existing bucket.

ts
get(name: string, bucketName: string)

Imagine you create a bucket in the dev stage. And in your personal stage frank, instead of creating a new bucket, you want to share the bucket from dev.

ts title="sst.config.ts"
const bucket = $app.stage === "frank"
  ? sst.aws.Bucket.get("MyBucket", "app-dev-mybucket-12345678")
  : new sst.aws.Bucket("MyBucket");

We are using $app.stage, a global to get the current stage the CLI is running on. It allows us to conditionally create the bucket.

Here app-dev-mybucket-12345678 is the auto-generated bucket name for the bucket created
in the dev stage. You can find this by outputting the bucket name in the dev stage.

ts title="sst.config.ts"
return {
  bucket: bucket.name
};

And it'll print it out on sst deploy.

bash frame="none"
bucket: app-dev-mybucket-12345678

You can read more about outputs in the run function.


Www Src Content Docs Docs State (`www/src/content/docs/docs/state.mdx`)


title: State

import VideoAside from "../../../components/VideoAside.astro";

When you deploy your app, SST creates a state file locally to keep track of the state of the infrastructure in your app.

So when you make a change, it'll allow SST to do a diff with the state and only deploy what's changed.


The state of your app includes:

  1. A state file for your resources. This is a JSON file.
  2. A passphrase that is used to encrypt the secrets in your state.

Aside from these, SST also creates some other resources when your app is first deployed. We'll look at this below.


The state is generated locally but is backed up to your provider using:

  1. A bucket to store the state, typically named sst-state-<hash>. This is created in the region of your home. More on this below.
  2. An SSM parameter to store the passphrase used to encrypt your secrets, under /sst/passphrase/<app>/<stage>. Also created in the region of your home.

:::danger
Do not delete the SSM parameter that stores the passphrase for your app.
:::

The passphrase is used to encrypt any secrets and sensitive information. Without it SST won't be able to read the state file and deploy your app.


Home

Your sst.config.ts specifies which provider to use for storing your state. We call this the home of your app.

ts title="sst.config.ts"
{
  home: "aws"
}

You can specify which provider you'd like to use for this. Currently aws and cloudflare are supported.

:::tip
Your state file is uploaded to your home.
:::

When you specify your home provider, SST assumes you'd like to use that provider in your app as well and adds it to your providers internally. So the above is equivalent to doing this.

ts title="sst.config.ts"
{
  home: "aws",
  providers: {
    aws: true
  }
}

This also means that if you change the region of the aws provider above, you are changing the region for your home as well.

You can read more about the home provider in Config.


Retention

By default, SST keeps every completed state snapshot. You can limit the number of snapshots kept for each stage with state.retention.

ts title="sst.config.ts"
{
  home: "aws",
  state: {
    retention: 30
  }
}

After an update completes, SST keeps the latest 30 snapshots and removes older snapshots with their matching event logs, update records, and summaries. The current state, secrets, and encryption passphrase are not removed.

Retention counts completed updates instead of days. If a stage is inactive, its retained snapshots do not expire. Pruning runs the next time an update completes for that stage.

:::caution
Lowering state.retention permanently removes older state history. On AWS, SST also removes the underlying S3 object versions.
:::

On AWS, the retained snapshots become the recovery history. SST removes noncurrent versions of the mutable current-state object, including the intermediate checkpoints uploaded during an update.


Bootstrap

As SST starts deploying the resources in your app, it creates some additional bootstrap resources. If your app has a Lambda function or a Docker container, then SST will create the following in the same region as the given resource:

  1. An assets bucket to store the function packages, typically named sst-asset-<hash>.
  2. An ECR repository to store container images, called sst-asset.
  3. An SSM parameter to store the assets bucket name and the ECR repository, stored under /sst/bootstrap.
  4. An AppSync Events API endpoint to power Live.

The SSM parameter is used to look up the location of these resources.

:::tip
Some additional bootstrap resources are created based on what your app is creating.
:::

When you remove an SST app, it does not remove the state or bootstrap resources. This is because it does not know if there are other apps that might be using this. So if you want to completely remove any SST created resources, you'll need to manually remove these in the regions you've deployed to.


Reset

If you accidentally remove the bootstrap resources the SST CLI will not be able to start up.

To fix this you'll need to reset your bootstrap resources.

  1. Remove the resources that are listed in the parameter. For example, the asset or state bucket. Or the ECR repository.
  2. Remove the SSM parameter.

Now when you run the SST CLI, it'll bootstrap your account again.


How it works

The state file is a JSON representation of all the low level resources created by your app. It is a cached version of the state of resources in the cloud provider.

So when you do a deploy the following happens.

  1. The components in the sst.config.ts get converted into low level resource definitions. These get compared to the the state file.
  2. The differences between the two are turned into API calls that are made to your cloud provider.
    • If there's a new resource, it gets created.
    • If a resource has been removed, it gets removed.
    • If there's a change in config of the resource, it gets applied.
  3. The state file is updated to reflect the new state of these resources. Now the sst.config.ts, the state file, and the resources in the cloud provider are all in sync.

Out of sync

This process works fine until you manually go change these resources through the cloud provider's console. This will cause the state and the resources to not be in sync anymore. This can cause an issue in some cases.

:::caution
If you manually change the resources in your cloud provider, they will go out of sync with your app's state.
:::

Let's look at a couple of scenarios.

Say we've deployed a Function with it set to { timeout: 10 seconds" }. At this point, the config, state, and resource are in sync.


Change the resource

  • We now go change the timeout to 20 seconds in the AWS Console.
    • The config and state are out of sync with the resource since they are still set to 10 seconds.
  • Now if we deploy our app, the config will be compared to the state.
    • It'll find no differences and so it won't update the resource.

The config and state will stay out of sync with the resource.


Change the config

  • If we change our config to { timeout: 30 seconds" } and do a deploy.
  • The config and state will have some differences.
  • SST will make a call to AWS to set the timeout of the resource to 30 seconds.
    • Once updated, it'll update the state file to match the current state of the resource.

The config, state, and resource are back being in sync.


Remove the resource

  • Next we go to the AWS Console and remove the function.
    • The config and state still have the function with the timeout set to 30 seconds.
  • If we change our config to { timeout: 60 seconds } and do a deploy.
  • The config and state will be different.
  • SST will make a call to update the timeout of the resource to 60 seconds.
    • But this call to AWS will fail because the function doesn't exist.

Your deploys will fail moving forward because your state shows that a resource exists but it doesn't anymore. To fix this, you'll need to refresh your state file.


Refresh

To fix scenarios where the resources in the cloud are out of sync with the state of your app, you'll need to run.

bash
sst refresh

This command does a couple of simple things:

  1. It goes through every single resource in your state.
  2. Makes a call to the cloud provider to check the resource.
    • If the configs are different, it'll update the state to reflect the change.
    • If the resource doesn't exist anymore, it'll remove it from the state.

:::note
The sst refresh does not make changes to the resources in the cloud provider.
:::

Now the state and the resources are in sync. So if we take the scenario from above where we removed the function from the AWS Console but not from our config or state. To fix it, we'll need to:

  • Run sst refresh
    • This will remove the function from the state as well.
  • Now if we change our config to { timeout: 60 seconds } and do a deploy.
  • The config and state will be compared and it'll find that a function with that config doesn't exist.
  • So SST will make a call to AWS to create a new function with the given config.

In general we do not recommend manually changing resources in a cloud provider since it puts your state out of sync. But if you find yourself in a situation where this happens, you can use the sst refresh command to put them back in sync.


Www Src Content Docs Docs Upgrade Aws Databases (`www/src/content/docs/docs/upgrade-aws-databases.mdx`)


title: Upgrade AWS Databases

Sometimes database components like Postgres and Mysql need to be upgraded as your application scales.

When you change fields like version or instance, SST applies the update on the next sst deploy.

By default, AWS performs the upgrade in place. This restarts the database and causes temporary downtime until your application can reconnect.

This guide covers the different strategies to minimize that downtime.


Multi-AZ

Postgres and Mysql support Multi-AZ deployments. AWS maintains a standby replica in a different availability zone. During the upgrade, changes are applied to the standby first, then it fails over. This reduces downtime to around 60-120 seconds instead of the full upgrade duration.

ts title="sst.config.ts" {4}
const database = new sst.aws.Postgres("MyDatabase", {
  vpc,
  version: "17",
  multiAz: true,
});

Multi-AZ roughly doubles the cost since a standby instance is always running. You can enable it temporarily for the upgrade and disable it after.

:::caution
Major version upgrades restart both instances, making Multi-AZ ineffective for reducing downtime.
:::

Enabling RDS Proxy through the proxy property can help further reduce failover downtime by detecting the new primary directly instead of waiting for DNS propagation. This can bring downtime down to just a few seconds.


Blue/Green

Postgres and Mysql support AWS RDS Blue/Green deployments through the blueGreen property. This creates a staging copy of your database, applies the changes there, and switches over with near-zero downtime.

ts title="sst.config.ts" {4}
const database = new sst.aws.Postgres("MyDatabase", {
  vpc,
  version: "17",
  blueGreen: true,
});

Your database endpoint stays the same throughout, so no application changes are needed. The overall deploy takes significantly longer but actual downtime is near zero.

:::caution
Blue/Green deployments are not compatible with databases that have read replicas or proxy enabled.
:::

Learn more in this Pulumi article.


Upgrading steps

Here's the recommended workflow for upgrading a production database.

1. Check compatibility

Not all version jumps are allowed. You might need to go through intermediate versions.

2. Pick a strategy

Use in-place if some downtime is fine, Multi-AZ to reduce it, or Blue/Green for the shortest switchover when supported.

Component In-place Multi-AZ Blue/Green
Postgres βœ“ βœ“ βœ“
Mysql βœ“ βœ“ βœ“
Aurora βœ“ βœ• βœ•
Redis βœ“ βœ• βœ•
OpenSearch βœ“ βœ• βœ•

3. Verify in a test stage

Apply the change in a test stage first to ensure the upgrade works as expected.

diff lang="ts" title="sst.config.ts"
const database = new sst.aws.Postgres("MyDatabase", {
    vpc,
-   version: "16",
+   version: "17",
+   blueGreen: true,
  });

4. Deploy to production

Finally, deploy the upgrade to your production stage.

ts title="sst.config.ts"
const database = new sst.aws.Postgres("MyDatabase", {
  vpc,
  version: "17",
  blueGreen: true,
});

Www Src Content Docs Dummy Markdown (`www/src/content/docs/dummy/markdown.mdx`)


title: Markdown draft: true

import { Tabs, TabItem } from '@astrojs/starlight/components';
import VideoAside from "../../../components/VideoAside.astro";

The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Starlight builds pages that are as lightweight as possible. For example, on a first visit, a user will download less than 50 KB of compressed data β€” just 2.5% of the HTTP archive median. With a good caching strategy, subsequent navigations can download as little as 10 KB.

Typography

Once you've added a couple of features, SST can help you connect them together. This is great because you won't need to hardcode anything in your app.

Here are some links, link-b, and a really really long link to look at.

Paragraph 1

Series' creator Stephen Hillenburg first became fascinated with the ocean as a child and began developing his artistic abilities at a young age. Although these interests would not overlap for some timeβ€”the idea of drawing fish seemed boring to himβ€”Hillenburg pursued both during college, majoring in marine biology and minoring in art. After graduating in 1984, he joined the Ocean Institute, an organization in Dana Point, California, dedicated to educating the public about marine science and maritime history.

Paragraph 2

While Hillenburg was there, his love of the ocean began to influence his artistry. He created a precursor to SpongeBob SquarePants: a comic book titled The Intertidal Zone used by the institute to teach visiting students about the animal life of tide pools.

Lorem Ipsum is a placeholder text commonly used in the design and printing industries. Despite its widespread use, the origins of Lorem Ipsum are somewhat mysterious. Several theories exist about who may have invented the text, but no one knows. Some people believe that the ancient Greeks or Romans created Lorem Ipsum, while others think a printer in the 16th century invented it. Regardless of who created Lorem Ipsum, it has become an essential tool for designers and typesetters worldwide.

Sub Heading

A large inspiration to Hillenburg was Ween's 1997 album The Mollusk, which had a nautical and underwater theme. Hillenburg contacted the band shortly after the album's release, explaining the baseline ideas for SpongeBob SquarePants, and also requested a song from the band, which they sent on Christmas Eve. This song was "Loop de Loop", which was used in the episode "Your Shoe's Untied".

Lorem Ipsum is a placeholder text that designers and publishers often use to fill space until the content is ready. It has been used for centuries in the printing industry, and its origins can be traced back to the 16th century when a printer named Aldus Manutius created a type specimen book that included a passage of dummy text. The text was based on the works of Cicero, a Roman philosopher and orator, and it has been used ever since as a standard placeholder text. Despite its long history, the exact origins of Lorem Ipsum are still somewhat unclear.

The text comprises words and phrases that have been scrambled and rearranged, making it appear as if it is real text without any actual meaning. It may not have any particular significance, but it does serve a useful purpose in helping designers to visualize how their designs will look with text in place.

Sub Heading

Derek Drymon, who served as creative director for the first three seasons, has said that Hillenburg wanted to surround himself with a "team of young and hungry people."

Colors

The series features "sky flowers" as a main setting material.

Text can be bold, italic, or strikethrough.

Line normal text color.

Line secondary text color.

Line dimmed text color.

Normal secondary dimmed

Code

Let's test some code.

Inline

You can specify lines of code inline, var a = "some variable name";. And it can be a part of a paragraph as well.

If every other word is an inline code block what does that do for us.

Let's bold some words. Also add some links; a short and a long one and a bold one.

Line

js
dateformat.i18n = require('./lang/' + l);

Blocks

Let's add one line above.

Let's add another line above.

js
// Javascript code with syntax highlighting.
var fun = function lang(l) {
  dateformat.i18n = require('./lang/' + l);
  return true;
};

Let's add one line below.

Let's add another line below.

Frame

Code blocks can be rendered inside a window-like frame.

js title="my-test-file.js"
console.log('Hello World!');

Bash

bash title="create a new project"
npm create sst

Bash Without Title

bash
npm create sst

Code Markers

Lines

Mark entire lines & line ranges using the { } marker.

js {3-4}
function demo() {
  // This line is not highlighted
  // This line (#2) and the next one are highlighted
  return 'This is line #3 of this snippet';
}

Selections

Mark selections of text using the " " marker or regular expressions.

js "individual terms" /even.*supported/
// Individual terms can be highlighted, too
function demo() {
  return 'Even regular expressions are supported';
}

Insert Deletes

Mark text or lines as inserted or deleted with ins or del.

js "return true;" ins="inserted" del="deleted"
function demo() {
  console.log('These are inserted and deleted marker types');
  // The return statement uses the default marker type
  return true;
}

Diffs

Combine syntax highlighting with diff-like syntax.

diff lang="js"
function thisIsJavaScript() {
    // This entire block gets highlighted as JavaScript,
    // and we can still add diff markers to it!
-   console.log('Old code to be removed')
+   console.log('New and shiny code!')
  }

Tabs

You can display a tabbed interface using the <Tabs> and <TabItem> components.

Sirius, Vega, Betelgeuse Io, Europa, Ganymede

Code Blocks

ts title="sst.config.ts"
export default $config({
  app(input) {
    return {
      name: "my-sst-app",
      home: "aws"
    };
  },
  async run() {
    const bucket = new sst.aws.Bucket("MyBucket");

    return {
      bucket: bucket.name
    };
  }
});

With Frame

bash
npm create astro@latest -- --template starlight
bash
pnpm create astro --template starlight
bash
yarn create astro --template starlight

Without Frame

bash frame="none"
npm create astro@latest -- --template starlight
bash frame="none"
pnpm create astro --template starlight
bash frame="none"
yarn create astro --template starlight

Overflow

js
{
  url: {
    cors: {
      allowOrigins: ["https://www.example.com", "http://localhost:60905", "http://localhost:60905"]
    }
  }
}

Lists

Ordered List

  1. First item
    1. First sub item
    2. Second sub item
  2. Second item
  3. Third item

Multi-Line

  1. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.
  2. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.
  3. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Long List

  1. Item
  2. Item
  3. Item
  4. Item
  5. Item
  6. Item
  7. Item
  8. Item
  9. Item
  10. Item
  11. Item
  12. Item

Paragraph

  1. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

    :::tip
    Make sure to read this extra resource on this topic.
    :::

    Here's some additional data on it that's relevant to the subject.

  2. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

    Here's some additional data on it that's relevant to the subject.

  3. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Unordered List

  • First item
    • First sub item
    • Second sub item
  • Second item
  • Third item

Multi-Line

  • The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.
  • The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.
  • The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Paragraph

  • The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

    :::tip
    Make sure to read this extra resource on this topic.
    :::

    Here's some additional data on it that's relevant to the subject.

  • The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

  • The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Dividers

Use the --- to add a divider. It inserts an <hr> tag.


Table

Let's test some tables. Testing the table and the spacing between it and the neighboring paragraphs.

Syntax Description
Header Title
Paragraph Text
Link URL

The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Images

Headings

You can structure content using a heading. Headings in Markdown are indicated by a number of # at the start of the line.

Heading 3

This is heading 3.

Heading 4

This is heading 4.

Blockquotes

This is a blockquote, which is commonly used when quoting another person or document.

Blockquotes are indicated by a > at the start of each line.

Asides

:::note
This is a note.
:::

:::tip
This is a tip. By specifying the ZipFile property within the Code property, specify index.function_name as the handler.
:::

:::caution
This is a caution.
:::

:::danger
This is a danger.
:::

:::tip
Here's a website you should probably check out β€” Google
:::

:::note
Here's a website with code should probably check out β€” Google
:::

Paragraphs

Let's add one line above.

Let's add another line above. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

:::caution
This is a caution aside that we can compare the font sizes to the blocks around it.
:::

:::tip
This is a tip aside to test what back to back aside blocks look like.
:::

Let's add one line below. Let's make this a paragraph. The more data a web page transfers, the more energy resources it requires. In April 2023, the median web page required a user to download more than 2,000 KB according to data from the HTTP Archive.

Let's add another line below.

Custom Titles

:::tip[Did you know?]
Astro helps you build faster websites with β€œIslands Architecture”.
:::

Video Asides

Extra

Sections

At The

Bottom

For The

Overflow

ts
new sst.cloudflare.StaticSite("Website", {
  domain: "example.com",
  notFound: "single-page-application",
  trailingSlash: "auto",
});

Www Src Content Docs Dummy Tsdoc (`www/src/content/docs/dummy/tsdoc.mdx`)


title: TSDoc draft: true

import Segment from '../../../components/tsdoc/Segment.astro';
import Section from '../../../components/tsdoc/Section.astro';
import NestedTitle from '../../../components/tsdoc/NestedTitle.astro';
import InlineSection from '../../../components/tsdoc/InlineSection.astro';

Returns a locale object for the specified definition with `locale.format` and `locale.formatPrefix` methods.

This section has more from about the FormatSpecifier.

:::note
You can also add notes. By specifying the ZipFile property within the Code property, specify index.function_name as the handler.
:::

Simple Example

Here's a simple example.

ts
new FormatSpecifier("param1");

Complicated Example

Here's a more complicated example.

ts
new FormatSpecifier("param1", "param2", "param3");

For more info refer to our other docs.


Constructor

```ts new FormatSpecifier(specifier, addend?): FormatSpecifier ```
#### Parameters

Returns FormatSpecifier[] | null

Methods

noop

```ts noop(): void ```

This function doesn't do anything.

simpleArgs

```ts simpleArgs(true, false ,true): void ```

This function takes some simple args.

#### Parameters
  • isTrue boolean
  • isFalse? boolean
  • isAny? boolean

fullArgs

```ts fullArgs(true, false ,true): FormatSpecifier ```

This function takes full args.

Type Object

  • prop1? boolean

    Default true

    A simple prop to add.

  • prop2? “x86_64” | “arm_64”

    Default Uses x86 by default x86_64. But this description of the default is really long and runs more than one single line.

    A complex prop to add with a description that is longer than one line. This matters because

    A simple example.

    ts
    { prop2: "x86_64" }

    A more complicated multi-line example.

    ts
    {
      prop2: "x86_64"
    }
  • prop3? FormatSpecifier[] | null

    Default true

    Just pass something in.

    ts
    {
      prop2: "x86_64"
    }

Returns FormatSpecifier[] | null

Here's an example.

diff lang="ts"
function demo() {
+   return fullArgs(true, false ,true);
  }

And another example.

ts {4}
const a = 1;
const b = a + 1;

fullArgs(true, false ,true);

Properties

primitiveProp

The ARN of the resource.

Type “x86_64” | “arm_64”

objectProp

A simple object prop.

Type Object

objectPropNested

A nested object prop.

Type Object

objectKey1

A simple object prop.

Type Output<Object>

objectKey1

A simple object prop.

Type Object

objectKey11

A simple object prop.

Type Object[]

objectKey112

A simple object prop.

Type Object

key1111

A simple prop.

Type string

DummyArgs

simpleArg

The CPU architecture of the lambda function.

Type “x86_64” | “arm_64”

Default Uses x86 by default x86_64. But this description of the default is really long and runs more than one single line.

For example,

ts
new Function(stack, "Function", {
  architecture: "arm_64",
})

objectPropSimple?

A prop that takes an object with simple keys.

Type Object

  • isTrue boolean
  • isFalse? boolean
  • isAny? boolean

Default undefined

ts
new Function(stack, "Function", {
  architecture: "arm_64",
})

objectPropFull?

A prop that takes an object with full keys.

Type Object

  • prop1? boolean

    Default true

    A simple prop to add.

    ts
    {
      prop1: true
    }
  • prop2? “x86_64” | “arm_64”

    Default Uses x86 by default x86_64. But this description of the default is really long and runs more than one single line.

    A complex prop to add with a description that is longer than one line. This matters because

    A simple example.

    ts
    { prop2: "x86_64" }

    A more complicated multi-line example.

    ts
    {
      prop2: "x86_64"
    }
  • prop3? FormatSpecifier[] | null

    Default true

    Just pass something in.

    ts
    {
      prop2: "x86_64"
    }

Default undefined

ts
new Function(stack, "Function", {
  architecture: "arm_64",
})

transform?

[Transform](/docs/transform/) how this component is created.

Type Object

Default undefined



template: splash title: Privacy Policy pagefind: false

import config from '../../../../config.ts';

Last updated: October 3, 2024

  1. Welcome

    Welcome to the Privacy Policy for sst.dev and the SST Console application(s) (together the β€œPlatform”), operated by Anomaly Innovations (hereafter referred to as β€œAI”, β€œwe”, β€œus”, and β€œour”). Your privacy is very important to us. This Privacy Policy explains how personal information about you is collected, used and disclosed by AI with respect to your use and interaction with AI, the Platform and our related services (the β€œServices”).

    We may make changes to the Privacy Policy from time to time, so please be sure to check the Privacy Policy regularly. If we decide to make material changes to the Privacy Policy, we will notify you and other users via email or by placing a notice on the Platform.

  2. How do you collect personal information?

    We collect personal information from you when you provide information directly to us, for example, when you contact us, register an account, purchase Services or make inquiries to our webmaster.

    We may collect personal information from third parties who you provided information to, such as providers related to our Services, or in connection with processing payment related to your subscription for our Services.

    We may also collect personal information of third parties that you provide to us or authorize us to collect (β€œThird Party Data”). You are responsible for obtaining the necessary consent for the collection and use of Third Party Data and for the disclosure of this data to us.

  3. What kind of personal information do you collect?

    Examples of personal information we collect and use are:

    • Contact information, such as full name, email address, telephone number
    • Company name
    • Amazon Web Services (β€œAWS”) Identity and Access Management (β€œIAM”) credentials
    • Git provider credentials (access tokens)
    • Payment information, including payment card data and billing address
    • Information you provide when you contact us, provide feedback, make a complaint or other inquiries
    • Other personal information required to provide you with our Services
    • Tracking data such as time spent on webpages, clicks, actions
  4. How do you use the information?

    AI uses your personal information to:

    • Register an account
    • Provide our Services
    • Administer, troubleshoot, enhance and manage our Services, including processing your subscriptions
    • Verify transactions and process payments
    • Send processing communications, including updates on your subscription or account
    • Send promotional communications such as special offers or other promotions (if you have opted-in to receive such communications)
    • Analyze the accuracy and effectiveness of our Platform and Services
    • Analyzing and understanding our customers and their needs
    • Meet obligations as required by applicable laws, i.e. fraud detection, enforcing the Terms of Use, enforcing any agreements, or when required by court order or an authorized enforcement agency
    • Tailor your experience while visiting the Platform
    • Responding to inquiries, complaints and other communications to AI
    • Other purposes that we identify to you from time to time

    It is important to understand that we will undertake every reasonable precaution to safeguard your personal information, however, providing this data to us may expose your personal information to various risks, including in the event of a data breach. If a data breach occurs, we will notify you as soon as possible and will comply with all legal requirements to mitigate any harms resulting from such a breach.

  5. Do you automatically collect technical data, and why?

    Like many websites, we automatically collect technical data when you visit and use our Platform.

    IP Addresses: We may collect your IP address when you visit our Platform to help us diagnose problems with our main computers, to administer the Platform and system, to report aggregated information to our partners, and to audit the use of our Platform. We do not normally link IP addresses to anything personally identifiable. In limited circumstances, we may use IP addresses to help us identify you when we feel it is necessary to enforce compliance with our Software as a Service Agreement or to protect our Services, the Platform, users or others.

    Cookies and similar technologies: We use β€œcookies”, scripts, pixel tags, etags, web beacons and other similar technologies on certain pages of our Platform. These automatically collect data from a visitor to our Platform and transfer this information to the visitor’s hard drive. Cookies help us recognize repeat visitors, personalize the Platform and communications and tailor content to match particular interests. We also use these technologies to track Platform usage trends and patterns.

    • Opt out of cookies. You are always free to set your browser to decline cookies and you can delete them from your computer. If you decline β€œcookies” or delete them from your computer, you will still be able to access our Platform, but you may find that some areas do not work as smoothly as when cookies are enabled on your system.
  6. Do you share personal information?

    We may share personal information with third party service providers who assist us in providing and administering the Platform and our Services. Third parties may assist us with services such as data hosting and marketing. These third parties that process personal information on our behalf are contractually obligated to safeguard information and only use the information provided to them for the purposes outlined in this Privacy Policy.

    We may share information with our business and promotional partners to send you information, by email, telephone or other means, about products and services you may like. Users may opt-out of receiving communications at any time. See Section 11 on how to opt-out of receiving promotional emails.

    AI may provide certain demographic information to third parties in connection with providing the Services to you.

    We may disclose and share your personal information to explore and/or undertake a corporate transaction, including a merger, acquisition, amalgamation, IPO, reorganization or sale of AI and/or the Platform. Your personal information will be used and shared solely for the purposes related to the transaction and will be protected by security safeguards appropriate to the sensitivity of the information.

    We may disclose personal information to government, regulatory and/or law enforcement agencies if we believe the disclosure is legally required, is appropriate in connection with an investigation of improper or illegal conduct in connection with our Platform and Services, or is otherwise required or permitted by law.

  7. Do you aggregate personal information?

    We may aggregate (i.e. assemble on a basis that does not enable one to personally identify you) the information we collect from you, including any technical data we automatically collect. Aggregated or β€œde-identified data” means that information that does not identify an individual and no longer considered β€œpersonal information”. Aggregated data may be used by us to improve our Services and for marketing or resale purposes, and may be shared with and used by advertisers and other third parties. We may also use aggregate data to determine what types of products and services are best suited for our customers and are requested by our customers. If we share aggregated data to third parties, personal information will not be individually identifiable in the aggregated data.

  8. Other websites and your information

    Our Platform may be linked from another website, or have links to other websites such as social media platforms. This Privacy Policy only relates to our collection and use of your information on the Platform and we are not responsible for the privacy policies and practices of other third party websites, including from any provided links. If you would like information on any other party’s privacy policy, you should contact that party directly, and we encourage you to do so.

  9. Security, storage and retention of your personal information

    AI employs reasonable measures to protect and ensure the security of your personal information from unauthorized use, access, disclosure, alteration, destruction or loss.

    Your personal information may be stored and/or processed or otherwise used by or on behalf of AI outside of your jurisdiction of residence. By using our Services, you consent that your personal information is subject to the authorities and laws of that jurisdiction.

    AI retains your personal information for as long as necessary or relevant for the listed purposes in this Privacy Policy, or as required by any applicable legal and retention obligations.

  10. How you can access, update and modify your information

    Updating information: If you have created an account, you can update or make changes to your personal information by logging into your account. If you do not have an account with AI, you can contact us at any of the options below.

    Access Request: If you make a request, we will let you know what personal information AI has about you, what it is being used for, and with whom it has been shared. If you notice any inaccuracies or wish to update any part of your personal information, we will look into that as well.

    AI will provide you with access to your personal information that is in our possession, subject to certain exceptions, including information that is referenced to another individual’s personal information that we cannot sever, or information subject to solicitor-client privilege, or other legal restrictions that may prevent us from fulfilling your access request. To inquire contact us at <a href={mailto:${config.email}}>{config.email} (include β€œAttention: Privacy Officer” in the subject line).

  11. How to opt-out of receiving promotional communications

    In the event you wish to unsubscribe from receiving promotional email communications from AI, you may opt-out by:

    • Removing yourself by clicking β€œunsubscribe” at the bottom of an email we send you, or
    • Contact us at <a href={mailto:${config.email}}>{config.email}

    If you opt-out of receiving marketing-related emails from us, we may still send you important administrative messages, such as notifications regarding your account or subscription to our Services, or as otherwise consistent with applicable law.

  12. Contact Us

    To contact us with questions about this Privacy Policy or other inquiries send us an email atΒ <a href={mailto:${config.email}}>{config.email}Β and write β€œPrivacy Policy” in the subject line.

Still Need Help? Email <a href={mailto:${config.email}}>{config.email}.



template: splash title: Terms of Service pagefind: false

import config from '../../../../config.ts';

PLEASE READ THIS AGREEMENT BEFORE USING THE SST CONSOLE. BY ACCESSING OR USING THE SERVICE (AS DEFINED BELOW), YOU (β€œyou” or the β€œCustomer”) SIGNIFY ACCEPTANCE OF AND AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT, DO NOT ACCESS OR USE THE SERVICE. IF THE PARTIES HAVE A FULLY EXECUTED AGREEMENT THAT EXPRESSLY GOVERNS ORDERS FOR THE SST CONSOLE SERVICE OFFERING, SUCH AGREEMENT SHALL SUPERSEDE THIS AGREEMENT.

This Software as a Service Agreement (the β€œAgreement”) governs Customer’s relationship with Anomaly Innovations (β€œAI”) relating to the Service offered by AI through the SST website located at https://sst.dev and the SST Console application(s) (together the β€œPlatform”) owned and operated by AI.

Please read this Agreement carefully and be sure that you fully understand the terms and conditions contained herein. This Agreement constitutes a binding legal agreement between you and AI.

Your use of the Service constitutes your agreement to all such terms, conditions, and notices in effect at such time. You hereby represent and warrant that (i) you are lawfully able to enter into and perform a legally binding contract, (ii) if you are entering into this Agreement on behalf of your employer you are authorized to do so, and (iii) you agree to be bound by this Agreement. Please print a copy of this Agreement and retain it for your or your employer’s records.

You should also read the SST Privacy Policy, which is incorporated by reference into this Agreement and available on the SST Platform. If you do not accept and agree to be bound by all of the terms of this Agreement, including the SST Privacy Policy, do not access or use the Service.

AI may update or revise this Agreement (including the SST Privacy Policy and the Fee Schedule) from time to time. You agree that you will review this Agreement periodically. You are free to decide whether or not to accept a modified version of this Agreement, but accepting this Agreement, as modified, is required for you to continue using the Service. You may have to click β€œaccept” or β€œagree” to show your acceptance of any modified version of this Agreement. If you do not agree to the terms of this Agreement or any modified version of this Agreement, your sole recourse is to terminate your use of the Service, in which case you will no longer have access to your Account (as defined below). Except as otherwise expressly stated by AI, any use of the Service is subject to the version of this Agreement in effect at the time of use.

The parties hereby agree to the following terms:

  1. DEFINITIONS

    β€œAggregated Data” means the aggregated, anonymized and de-identified Customer Data.

    β€œService” means the service provided by AI the details of which are set forth in Section 2 hereof.

    β€œCustomer Data” means all information and data that the Customer uploads to or collects or transmits via the Service, including metadata.

    β€œDocumentation” means the user guides, online help, release notes, training materials and other documentation provided or made available by AI to the Customer regarding the use or operation of the Services.

    β€œFee Schedule” means the schedule on the Platform that outlines the fees for the Service, including the Subscription Fee for each Subscription Plan.

    β€œSoftware” means the object code version of any software to which Customer is provided access as part of the Service, including any updates or new versions.

    β€œSubscription Fee” means the amount charged by AI to the Customer for a Subscription Plan.

    β€œSubscription Plan” means a fixed term plan for use of the Service.

  2. THE SERVICE

    2.1 The SST Console Service is a fully-configured cloud-based continuous integration and continuous delivery pipeline for building, testing, deploying, and monitoring SST applications on Amazon Web Services (β€œAWS”) and other cloud providers.

    2.2 AI may make modifications to any aspect of the Service from time to time, to add or remove features and functionalities.

  3. USE OF THE SERVICE

    3.1 In order to use the Service, the Customer must register for an account on the Platform (β€œSST Console Account” or β€œAccount”) and subscribe to the Service (a β€œSubscription”). As part of the registration process, the Customer will be required to provide AI with certain information (such as identification or contact details). The Customer agrees that any registration information provided to AI will always be accurate, correct and up to date.

    3.2 In order to use the Service, Customers must connect their Account to a Git provider and provide AI with AWS Identity and Access Management (β€œIAM”) credentials.

    3.3 Subject to the Customer’s timely payment of all applicable Subscription Fees, the Customer will, during the term of this Agreement, receive a nonexclusive, non-transferable, worldwide right to access and use the Service subject to the terms and conditions of this Agreement.

    3.4 The Customer acknowledges that this Agreement is a services agreement and that AI will not deliver copies of the Software to the Customer as part of the Service.

    3.5 AI or its licensors retain all right, title and interest, including intellectual property rights, in and to the Software, the Service, Documentation and other deliverables provided under this Agreement, including all modifications, improvements, upgrades, derivative works and feedback related thereto. The Customer agrees to assign all right, title and interest it may have in the foregoing to AI.

    3.6 Customer agrees that it will only access and use the Service by the means described in the Documentation. Customer agrees that it will not attempt to circumvent any limitations on its use of the Service without AI’s express consent.

    3.7 Customer hereby acknowledges and agrees that AI may monitor and record the Customer’s use of the Service to ensure that the Customer complies with the terms and conditions of this Agreement.

  4. RESTRICTIONS

    The Customer shall not, and shall not permit anyone to: (i) copy or republish the Service or Software, (ii) make the Service available to any person other than Customer’s registered users, (iii) use or access the Service to provide service bureau, time-sharing or other computer hosting services to third parties, (iv) modify or create derivative works based upon the Service or Documentation, (v) remove, modify or obscure any copyright, trademark or other proprietary notices contained in the Software used to provide the Service or in the Documentation, (vi) reverse engineer, decompile, disassemble, or otherwise attempt to derive the source code of the Software used to provide the Service, except and only to the extent such activity is expressly permitted by applicable law, or (vii) access the Service or use the Documentation in order to build a similar product or competitive product.

  5. CUSTOMER RESPONSIBILITIES

    5.1 Compliance with Laws. The Customer shall comply with all applicable local, state, national and foreign laws in connection with its use of the Service, including those laws related to spam, data privacy, international communications, and the transmission of technical or personal data. The Customer acknowledges that AI exercises no control over the content of the information transmitted by the Customer through the Service. The Customer shall not upload, post, reproduce or distribute any information, software or other material protected by copyright, privacy rights, or any other intellectual property right without first obtaining the permission of the owner of such rights.

    5.2 Unauthorized Use; False Information. The Customer shall: (a) notify AI immediately of any unauthorized use of any password or user id or any other known or suspected breach of security, (b) report to AI immediately and use reasonable efforts to stop any unauthorized use of the Service that is known or suspected by the Customer, and (c) not provide false identity information to gain access to or use the Service.

    5.3 Insurance. The Customer is responsible for obtaining and maintaining levels of and types of insurance coverage that are appropriate for its business operations.

  6. CUSTOMER DATA

    6.1 Customer Responsibility. The Customer is solely responsible for all Customer Data stored on the Service, and for ensuring that the Customer Data does not (i) include anything that actually or potentially infringes or misappropriates the copyright, trade secret, trademark or other intellectual property right of any third party, or (ii) contain anything that is obscene, defamatory, harassing, offensive or malicious. The Customer is solely responsible for the accuracy, timeliness, completeness and usefulness of Customer Data.

    6.2 Ownership. The Customer retains ownership of its Customer Data, including all intellectual property rights therein and relating thereto.

    6.3 License from Customer. The Customer grants AI a non-exclusive license to use, copy, store, configure, display, edit, modify, reproduce, distribute and transmit Customer Data for the sole purpose of providing the Service to the Customer.

    6.4 Aggregated Data. The Customer grants AI a non-exclusive, transferable, assignable, irrevocable, royalty-free, worldwide, perpetual license to create Aggregated Data and to use such Aggregated Data, and all modifications thereto and derivatives thereof , for any purpose, including, without limitation, to improve the Service, develop new products and services and understand usage. AI shall own all Aggregated Data and may transfer or assign any of its rights in the Aggregated Data to any third party.

    6.5 Transfer Abroad. AI may transfer Customer Data outside of Customer’s jurisdiction of residence for processing and storage.

    6.6 Security. AI will have in place and maintain appropriate technical and organizational measures to protect Customer Data against unauthorized access, or accidental loss, destruction or damage.

    6.7 Termination. In the event that this Agreement is terminated or if the Customer removes any Customer Data from the Customer’s Account, AI may retain Aggregated Data and may continue to use the Aggregated Data pursuant to the licenses granted above.

  7. BILLING AND PAYMENT

    7.1 Free Trial. AI may offer the Customer a free trial Subscription. Free trials are for new Customers only. The free trial period lasts until the Customer exceeds a prescribed limit on the number of deployments per month (β€œTrial Period”), or as otherwise specified to the Customer. At end of the Trial Period, the free trial Subscription will come to an end and the Customer must purchase a Subscription Plan to continue to use the Service.

    7.2 Subscription Plans. AI offers Customers various subscription plans for the use of the Service (each a β€œSubscription Plan”). A description of each Subscription Plan is set out in the Fee Schedule, which can be found on the SST website at https://sst.dev/docs/console#pricing (the β€œFee Schedule”). AI may also offer certain enterprise Customers custom-made Subscription Plans.

    7.3 Subscription Fees. The monthly subscription fee (the β€œSubscription Fee”) for each Subscription Plan is set out in the Fee Schedule.

    7.4 Billing and Payment. When the Customer purchases a Subscription, the Customer authorizes AI to charge the Customer the Subscription Fee for the corresponding Subscription Plan to the Customer’s designated payment method. The Subscription Fee will be charged in arrears to the Customer on a monthly recurring basis on the last day of each month or as otherwise specified by AI (the β€œRenewal Date”). The Customer will be charged a prorated amount of the Subscription Fee for the first month of a Subscription Plan where applicable.

    7.5 Taxes. AI shall bill the Customer for applicable taxes as a separate line item on each invoice. The Customers shall be responsible for payment of all sales and use taxes, value added taxes (VAT), or similar charges relating to the use of the Service.

    7.6 Renewal. Each Subscription will automatically renew and continue month-to-month unless and until the Customer cancels the Subscription. The Customer must cancel the Subscription before the Renewal Date to avoid being billed for the renewal.

    7.7 Cancellation. Upon cancellation of the Subscription, the Customer will continue to have access to the Service through the end of the monthly billing period.

    7.8 No Refunds. All Fee payments are non-refundable and there are no refunds or credits for partially used periods.

    7.9 Price and Plan Changes. AI reserves the right to adjust the Subscription Plans, the Fee Schedule and the pricing of any Service in any manner and at any time in its sole discretion. Any price changes to existing Subscriptions will take effect following email notice to the Customer.

  8. TERM AND TERMINATION

    8.1 Term of Agreement. The term of this Agreement shall begin when the Customer registers for a SST Console Account and shall continue until the Agreement is terminated by either party as outlined in this Section.

    8.2 Termination. AI may terminate this Agreement at any time without notice for any reason, with or without cause, in its sole discretion. The Customer may terminate this Agreement at any time by following the instructions in the Customer’s SST Console Account. This Agreement will automatically terminate when the Customer cancels the Customer’s Account.

    8.3 Suspension of Service. AI reserves the right to suspend the Service for non-payment of the Subscription Fees or for any other reason, with or without cause, in its sole discretion. Suspension of the Service shall not release the Customer of its payment obligations under this Agreement.

    8.4 Effect of Termination. Upon termination of this Agreement, AI shall immediately cease providing the Service and all usage rights granted under this Agreement shall terminate. After any suspension or termination, the Customer may or may not be granted permission to use the Service or to reactivate the Customer’s Account or Subscription. The Customer understands that the termination of this Agreement may involve the deletion of Customer Data stored in the Customer’s Account. AI shall not be liable to the Customer or to any third party for any liabilities, claims or expenses arising from or relating to any termination or suspension.

    8.5 No Refund of Fees. In the event of termination or suspension of the Service, the Customer shall not be entitled to any refund or partial refund of any Subscription Fees paid to AI, including any prepayments.

  9. WARRANTIES

    9.1 Warranty. AI represents and warrants that it will provide the Service in a professional manner consistent with general industry standards. Customer’s exclusive remedy for a breach of this warranty shall be as provided in Section 8, Term and Termination.

    9.2 Warranty Disclaimer. TO THE EXTENT PERMITTED BY APPLICABLE LAW AND Except as expressly provided herein, the service IS provided to CUSTOMER on an "AS IS" basis without any warranty whatsoever AND AI EXPRESSLY DISCLAIMS ALL OTHER REPRESENTATIONS, WARRANTIES, AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY REPRESENTATION, WARRANTY OR CONDITION OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, OR NON-INFRINGEMENT, OR ANY WARRANTY ARISING FROM A COURSE OF DEALING, PERFORMANCE, OR TRADE USAGE. CUSTOMER’S sole and exclusive remedy, and AI’S sole obligation to CUSTOMER or any third party for any claim arising out of CUSTOMER’S use of the service, is that CUSTOMER IS free to discontinue ITS use of the service at any time.

  10. LIMITATIONS OF LIABILITY

    10.1 Limitation of Liability. TO THE EXTENT PERMITTED BY APPLICABLE LAW AND EXCEPT FOR INDEMNIFICATION CLAIMS UNDER SECTION 11, THE MAXIMUM AGGREGATE LIABILITY OF AI TO THE CUSTOMER ARISING OUT OF OR RELATED TO THIS AGREEMENT (WHETHER IN CONTRACT OR TORT OR UNDER ANY OTHER THEORY OF LIABILITY) SHALL NOT EXCEED THE AMOUNT OF FEES ACTUALLY PAID BY CUSTOMER TO AI DURING THE 12 MONTHS PRECEDING THE DATE OF THE EVENT GIVING RISE TO THE CLAIM.

    10.2 Exclusion of Consequential and Related Damages. IN NO EVENT SHALL EITHER PARTY HAVE ANY LIABILITY TO THE OTHER FOR ANY LOST PROFITS OR REVENUES OR FOR ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES HOWEVER CAUSED, WHETHER IN CONTRACT, TORT OR UNDER ANY OTHER THEORY OF LIABILITY, AND WHETHER OR NOT SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

    10.3 Connectivity. AI SHALL NOT BE LIABLE FOR DELAYS, INTERRUPTIONS, SERVICE FAILURES, OR OTHER PROBLEMS INHERENT IN USE OF THE INTERNET, ELECTRONIC COMMUNICATIONS, TELECOMMUNICATIONS NETWORKS OR OTHER SYSTEMS OR NETWORKS OUTSIDE THE REASONABLE CONTROL OF AI.

  11. INDEMNIFICATION

    11.1 Indemnification by AI. If a third party makes a claim against the Customer that the Service infringes any patent, copyright or trademark, or misappropriates any trade secret, or that AI’s negligence or willful misconduct has caused bodily injury or death, AI shall defend the Customer and its directors, officers and employees against the claim at AI’s expense and AI shall pay all losses, damages and expenses (including reasonable legal fees) finally awarded against such parties or agreed to in a written settlement agreement signed by AI, to the extent arising from the claim. AI shall have no liability for any claim based on (a) Customer Data, (b) modification of the Service not authorized by AI, or (c) use of the Service other than in accordance with the Documentation and this Agreement. AI may, at its sole option and expense, procure for the Customer the right to continue use of the Service, modify the Service in a manner that does not materially impair the functionality, or as its sole obligation and liability, terminate the Service and repay to Customer any Subscription Fees paid by Customer with respect to the Service for any period following the termination date.

    11.2 Indemnification by Customer. If a third party makes a claim against AI that the Customer Data infringes any patent, copyright or trademark, or misappropriates any trade secret, the Customer shall defend AI and its directors, officers and employees against the claim at the Customer’s expense and the Customer shall pay all losses, damages and expenses (including reasonable attorneys’ fees) finally awarded against such parties or agreed to in a written settlement agreement signed by the Customer, to the extent arising from the claim.

    11.3 Conditions for Indemnification. A party seeking indemnification under this section shall (a) promptly notify the other party of the claim, (b) give the other party sole control of the defense and settlement of the claim, and (c) provide, at the other party’s expense for out-of-pocket expenses, the assistance, information and authority reasonably requested by the other party in the defense and settlement of the claim.

  12. GENERAL PROVISIONS

    12.1 Non-Exclusive Service. The Customer acknowledges that the Service is provided on a non-exclusive basis. Nothing shall be deemed to prevent or restrict AI’s ability to provide the Service or other technology, including any features or functionality first developed for Customer, to other parties.

    12.2 Assignment. Neither party may assign this Agreement or any right under this Agreement, without the consent of the other party, which consent shall not be unreasonably withheld or delayed; provided however, that either party may assign this Agreement to an acquirer of all or substantially all of the business of such party to which this Agreement relates, whether by merger, asset sale or otherwise. This Agreement shall be binding upon and inure to the benefit of the parties’ successors and permitted assigns.

    12.3 Subcontractors. AI may employ subcontractors in performing its duties under this Agreement, provided, however, that AI shall not be relieved of any obligation under this Agreement.

    12.4 Notices. Except as otherwise permitted in this Agreement, notices under this Agreement shall be made in writing to the other party.

    12.5 Force Majeure. Each party will be excused from performance for any period during which, and to the extent that, such party or any subcontractor is prevented from performing any obligation or Service, in whole or in part, as a result of causes beyond its reasonable control, and without its fault or negligence, including without limitation, acts of God, strikes, lockouts, riots, acts of terrorism or war, epidemics, communication line failures, and power failures.

    12.6 Waiver. No waiver shall be effective unless it is in writing and signed by the waiving party. The waiver by either party of any breach of this Agreement shall not constitute a waiver of any other or subsequent breach.

    12.7 Severability. If any term of this Agreement is held to be invalid or unenforceable, that term shall be reformed to achieve as nearly as possible the same effect as the original term, and the remainder of this Agreement shall remain in full force.

    12.8 Entire Agreement. This Agreement (including all Schedules and exhibits) contains the entire agreement of the parties and supersedes all previous oral and written communications by the parties, concerning the subject matter of this Agreement. This Agreement may be amended solely in a writing signed by both parties. Standard or printed terms contained in any purchase order or sales confirmation are deemed rejected and shall be void unless specifically accepted in writing by the party against whom their enforcement is sought; mere commencement of Services or payment against such forms shall not be deemed acceptance of the terms.

    12.9 Survival. Sections 4, 6, 8, and 9 through 12 of this Agreement shall survive the expiration or termination of this Agreement for any reason.

    12.10 Publicity. AI may include the Customer’s name and logo in its customer lists and on its website.

    12.11 Independent Contractor. The parties have the status of independent contractors, and nothing in this Agreement nor the conduct of the parties will be deemed to place the parties in any other relationship. Except as provided in this Agreement, neither party shall be responsible for the acts or omissions of the other party or the other party’s personnel.

    12.12 Governing Law. This Agreement shall be governed by the laws of the State of Delaware and the federal laws applicable therein.

    12.13 Compliance with Laws. AI shall comply with all applicable local, state, national and foreign laws in connection with its delivery of the Services, including those laws related to data privacy, international communications, and the transmission of technical or personal data.

    12.14 Dispute Resolution. Customer’s satisfaction is an important objective to AI in performing its obligations under this Agreement. Except with respect to intellectual property rights, if a dispute arises between the parties relating to the interpretation or performance of this Agreement or the grounds for the termination hereof, the parties agree to hold a meeting within fifteen (15) days of written request by either party, attended by individuals with decision-making authority, regarding the dispute, to attempt in good faith to negotiate a resolution of the dispute prior to pursuing other available remedies. If, within 15 days after such meeting, the parties have not succeeded in resolving the dispute, either party may protect its interests by any lawful means available to it.


Www Src Content Docs Docs Reference Sdk (`www/src/content/docs/docs/reference/sdk.mdx`)


title: SDK

The SST SDK allows your runtime code to interact with your infrastructure in a typesafe way.

You can use the SDK in your functions, frontends, and container applications. You can access links from components. And some components come with SDK clients that you can use.

:::tip
Check out the SDK section in a component's API reference doc.
:::

Currently, the SDK is only available for JS/TS, Python, Golang, and Rust. Support for other languages is on the roadmap.


Node.js

The JS SDK is an npm package that you can install in your functions, frontends, or container applications.

bash
npm install sst

Import Resource to access the linked resources.

js title="src/lambda.ts"
import { Resource } from "sst";

console.log(Resource.MyBucket.name);

:::tip
The Resource object is typesafe and will autocomplete the available resources in your editor.
:::

Here we are assuming that a bucket has been linked to the function. Here's what that could look like.

js title="sst.config.ts" {5}
const bucket = new sst.aws.Bucket("MyBucket");

new sst.aws.Function("MyFunction", {
  handler: "src/lambda.handler",
  link: [bucket]
});

Defaults

By default, the Resource object contains Resource.App. This gives you some info about the current app including:

  • App.name: The name of your SST app.
  • App.stage: The current stage of your SST app.
ts title="src/lambda.ts"
import { Resource } from "sst";

console.log(Resource.App.name, Resource.App.stage);

Clients

Components like the Realtime component come with a client that you can use.

ts title="src/lambda.ts"
import { realtime } from "sst/aws/realtime";

export const handler = realtime.authorizer(async (token) => {
  // Validate the token
});

For example, realtime.authorizer lets you create the handler for the authorizer function that Realtime needs.


How it works

In the above example, Resource.MyBucket.name works because it's been injected into the function package on sst dev and sst deploy.

For functions, this is injected into the globalThis using esbuild and for frontends, it's injected into the process.env object.

The JS SDK first checks the process.env and then the globalThis for the linked resources. You can read more about how the links are injected.


Python

SST uses uv to package your Python functions. Your functions need to be in a uv workspace.

To use the SDK, add it to your pyproject.toml.

toml title="functions/pyproject.toml"
[project]
dependencies = ["sst-sdk"]

[tool.uv.sources]
sst-sdk = { git = "https://github.com/anomalyco/sst.git", subdirectory = "sdk/python", branch = "dev" }

And in your function, import the resource module and access the linked resource.

py title="functions/src/functions/api.py" {1} "resource.mybucket.name"
from sst import Resource

def handler(event, context):
    print(Resource.MyBucket.name)

Here MyBucket is the name of a bucket that's linked to the function.

ts title="sst.config.ts" {6}
const bucket = new sst.aws.Bucket("MyBucket");

new sst.aws.Function("MyFunction", {
  handler: "functions/src/functions/api.handler",
  runtime: "python3.13",
  link: [bucket]
});

Client functions are currently not supported in the Python SDK.


Golang

Use the SST Go SDK package in your Golang functions or container applications.

go title="src/main.go"
import (
  "github.com/sst/sst/v3/sdk/golang/resource"
)

In your runtime code, use the resource.Get function to access the linked resources.

go title="src/main.go"
resource.Get("MyBucket", "name")

Where MyBucket is the name of a bucket that's linked to the function.

js title="sst.config.ts" {5}
const bucket = new sst.aws.Bucket("MyBucket");

new sst.aws.Function("MyFunction", {
  handler: "./src",
  link: [bucket]
});

You can also access the current app's info with.

go title="src/main.go"
resource.Get("App", "name")
resource.Get("App", "stage")

Client functions are currently not supported in the Go SDK.


Rust

Use the SST Rust SDK package in your Rust functions or container applications.

bash
cargo install sst_sdk

In your runtime, use the Resource::get() function to access linked resources as a typesafe struct, or a serde_json::Value.

rust title="main.rs"
use sst_sdk::Resource;

#[derive(serde::Deserialize, Debug)]
struct Bucket {
    name: String,
}

fn main() {
    let resource = Resource::init().unwrap();
    // access your linked resources as a typesafe struct that implements Deserialize
    let Bucket { name } = resource.get("MyBucket").unwrap();
    // or as a weakly typed json value (that also implements Deserialize)
    let openai_key: serde_json::Value = resource.get("OpenaiSecret").unwrap();
}

where MyBucket and OpenaiSecret are linked to the function.

ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket");
const openai = new sst.Secret("OpenaiSecret");

new sst.aws.Function("MyFunction", {
  handler: "./",
  link: [bucket, openai],
  runtime: "rust"
});

Client functions are currently not supported in the Rust SDK.


Www Src Content Docs Docs Start Aws Analog (`www/src/content/docs/docs/start/aws/analog.mdx`)


title: Analog on AWS with SST

We are going to create an Analog app, add an S3 Bucket for file uploads, and deploy it to AWS using SST.

:::tip[View source]
You can view the source of this example in our repo.
:::

Before you get started, make sure to configure your AWS credentials.


1. Create a project

Let's start by creating our project.

bash
npm create analog@latest
cd aws-analog

We are picking the Full-stack Application option and not adding Tailwind.


Init SST

Now let's initialize SST in our app.

bash
npx sst@latest init
npm install

Select the defaults and pick AWS. This'll create a sst.config.ts file in your project root.

It'll also ask you to update your vite.config.ts with something like this.

diff lang="ts" title="vite.config.ts"
plugins: [analog({
+  nitro: {
+    preset: "aws-lambda",
+  }
})],

Start dev mode

Run the following to start dev mode. This'll start SST and your Analog app.

bash
npx sst dev

Once complete, click on MyWeb in the sidebar and open your Analog app in your browser.


2. Add an S3 Bucket

Let's allow public access to our S3 Bucket for file uploads. Update your sst.config.ts.

js title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
  access: "public"
});

Add this above the Analog component.

Now, link the bucket to our Analog app.

js title="sst.config.ts" {2}
new sst.aws.Analog("MyWeb", {
  link: [bucket],
});

3. Generate a pre-signed URL

When our app loads, we'll generate a pre-signed URL for the file upload on the server. Create a new src/pages/index.server.ts with the following.

ts title="src/pages/index.server.ts" {10}
import { Resource } from 'sst';
import { PageServerLoad } from '@analogjs/router';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

export const load = async ({ }: PageServerLoad) => {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    // @ts-ignore: Generated on deploy
    Bucket: Resource.MyBucket.name,
  });

  const url = await getSignedUrl(new S3Client({}), command);

  return {
    url
  };
};

:::tip
We are directly accessing our S3 bucket with Resource.MyBucket.name.
:::

And install the npm packages.

bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

4. Create an upload form

Add the upload form client in src/pages/index.page.ts. Replace it with the following.

ts title="src/pages/index.page.ts" {6,20}
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { injectLoad } from '@analogjs/router';
import { toSignal } from '@angular/core/rxjs-interop';

import { load } from './index.server';

@Component({
  selector: 'app-home',
  standalone: true,
  imports: [FormsModule],
  template: `
    <form (ngSubmit)="onSubmit($event)">
      <input type="file" name="file">
      <button type="submit">Upload</button>
    </form>
  `,
})
export default class HomeComponent {
  data = toSignal(injectLoad<typeof load>(), { requireSync: true });

  async onSubmit(event: Event): Promise<void> {
    const file = (event.target as HTMLFormElement)['file'].files?.[0]!;

    const image = await fetch(this.data().url, {
      body: file,
      method: 'PUT',
      headers: {
        'Content-Type': file.type,
        'Content-Disposition': `attachment; filename="${file.name}"`,
      },
    });

    window.location.href = image.url.split('?')[0];
  }
}

Here we are injecting the pre-signed URL from the server into the component.

Head over to the local Analog app site in your browser, http://localhost:5173 and try uploading an image. You should see it upload and then download the image.


5. Deploy your app

Now let's deploy your app to AWS.

bash
npx sst deploy --stage production

You can use any stage name here but it's good to create a new stage for production.


Connect the console

As a next step, you can setup the SST Console to git push to deploy your app and monitor it for any issues.

You can create a free account and connect it to your AWS account.


Www Src Content Docs Docs Start Aws Angular (`www/src/content/docs/docs/start/aws/angular.mdx`)


title: Angular on AWS with SST

We are going to create an Angular 18 SPA, add an S3 Bucket for file uploads, and deploy it to AWS using SST.

:::tip[View source]
You can view the source of this example in our repo.
:::

Before you get started, make sure to configure your AWS credentials.


1. Create a project

Let's start by creating our project.

bash
npm install @angular/cli
ng new aws-angular
cd aws-angular

We are picking CSS for styles, and not using SSR.


Init SST

Now let's initialize SST in our app.

bash
npx sst@latest init

This'll create a sst.config.ts file in your project root.


2. Add an S3 Bucket

Let's allow public access to our S3 Bucket for file uploads. Update your sst.config.ts.

ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
  access: "public"
});

Add this above the StaticSite component.

We are going to upload a file to this bucket using a pre-signed URL. This'll let us upload it directly to our bucket.


3. Add an API

Let's create a simple API to generate that URL. Add this below the Bucket component.

ts title="sst.config.ts" {3}
const pre = new sst.aws.Function("MyFunction", {
  url: true,
  link: [bucket],
  handler: "functions/presigned.handler",
});

We are linking our bucket to this function.


Pass the API URL

Now, pass the API URL to our Angular app. Add this below the build prop in our StaticSite component.

ts title="sst.config.ts" {2}
environment: {
  NG_APP_PRESIGNED_API: pre.url
}

To load this in our Angular app, we'll use the @ngx-env/builder package.

bash
ng add @ngx-env/builder

Start dev mode

Run the following to start dev mode. This'll start SST and your Angular app.

bash
npx sst dev

Once complete, click on MyWeb in the sidebar and go to your Angular app in your browser. Typically on http://localhost:4200.


3. Create an upload form

Let's create a component to do the file upload. Add the following to src/app/file-upload.component.ts.

ts title="src/app/file-upload.component.ts" {19}
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-file-upload',
  standalone: true,
  imports: [FormsModule],
  template: `
    <form (ngSubmit)="onSubmit($event)">
      <input type="file" name="file">
      <button type="submit">Upload</button>
    </form>
  `,
})
export class FileUploadComponent {
  private http = inject(HttpClient);

  presignedApi = import.meta.env['NG_APP_PRESIGNED_API'];

  async onSubmit(event: Event): Promise<void> {
    const file = (event.target as HTMLFormElement)['file'].files?.[0]!;

    this.http.get(this.presignedApi, { responseType: 'text' }).subscribe({
      next: async (url: string) => {
        const image = await fetch(url, {
          body: file,
          method: "PUT",
          headers: {
            "Content-Type": file.type,
            "Content-Disposition": `attachment; filename="${file.name}"`,
          },
        });

        window.location.href = image.url.split("?")[0];
      },
    });
  }
}

This is getting the pre-signed API URL from the environment. Making a request to it to get the pre-signed URL and then uploading our file to it.

Let's add some styles below the template prop.

ts title="src/app/file-upload.component.ts"
styles: [`
  form {
    color: white;
    padding: 2rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: #23262d;
    background-image: none;
    background-size: 400%;
    border-radius: 0.6rem;
    background-position: 100%;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  }
  button {
    appearance: none;
    border: 0;
    font-weight: 500;
    border-radius: 5px;
    font-size: 0.875rem;
    padding: 0.5rem 0.75rem;
    background-color: white;
    color: black;
  }
  button:active:enabled {
    background-color: #EEE;
  }
`]

To make HTTP fetch requests we need to add the provider to our Angular app config. Add the following to the providers list in src/app/app.config.ts.

ts title="src/app/app.config.ts"
provideHttpClient(withFetch())

And import it at the top.

ts title="src/app/app.config.ts"
import { provideHttpClient, withFetch } from '@angular/common/http';

Let's add this to our app. Replace the src/app/app.component.ts file with.

ts title="src/app/app.component.ts"
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { FileUploadComponent } from './file-upload.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, FileUploadComponent],
  template: `
    <main>
      <app-file-upload></app-file-upload>
    </main>
    <router-outlet></router-outlet>
  `,
  styles: [`
    main {
      margin: auto;
      padding: 1.5rem;
      max-width: 60ch;
    }
  `],
})
export class AppComponent { }

4. Generate a pre-signed URL

Let's implement the API that generates the pre-signed URL. Create a functions/presigned.ts file with the following.

ts title="functions/presigned.ts" {8}
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

export async function handler() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });

  return {
    statusCode: 200,
    body: await getSignedUrl(new S3Client({}), command),
  };
}

:::tip
We are directly accessing our S3 bucket with Resource.MyBucket.name.
:::

And install the npm packages.

bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

Head over to the local Angular app in your browser, http://localhost:4200 and try uploading an image. You should see it upload and then download the image.


5. Deploy your app

Now let's deploy your app to AWS.

bash
npx sst deploy --stage production

You can use any stage name here but it's good to create a new stage for production.


Connect the console

As a next step, you can setup the SST Console to git push to deploy your app and monitor it for any issues.

You can create a free account and connect it to your AWS account.


Www Src Content Docs Docs Start Aws Astro (`www/src/content/docs/docs/start/aws/astro.mdx`)


title: Astro on AWS with SST

There are two ways to deploy an Astro site to AWS with SST.

  1. Serverless
  2. Containers

We'll use both to build a couple of simple apps below.


Examples

We also have a few other Astro examples that you can refer to.


Serverless

We are going to create an Astro site, add an S3 Bucket for file uploads, and deploy it using the Astro component.

:::tip[View source]
You can view the source of this example in our repo.
:::

Before you get started, make sure to configure your AWS credentials.


1. Create a project

Let's start by creating our project.

bash
npm create astro@latest aws-astro
cd aws-astro

We are picking all the default options.


Init SST

Now let's initialize SST in our app.

bash
npx sst@latest init
npm install

Select the defaults and pick AWS. This'll create a sst.config.ts file in your project root.

It'll also ask you to update your astro.config.mjs with something like this.

diff lang="js" title="astro.config.mjs"
+ import aws from "astro-sst";

export default defineConfig({
+  output: "server",
+  adapter: aws()
});

Start dev mode

Run the following to start dev mode. This'll start SST and your Astro site.

bash
npx sst dev

Once complete, click on MyWeb in the sidebar and open your Astro site in your browser.


2. Add an S3 Bucket

Let's allow public access to our S3 Bucket for file uploads. Update your sst.config.ts.

js title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
  access: "public"
});

Add this above the Astro component.

Now, link the bucket to our Astro site.

js title="sst.config.ts" {2}
new sst.aws.Astro("MyWeb", {
  link: [bucket],
});

3. Create an upload form

Add the upload form client in src/pages/index.astro. Replace the <Layout /> component with:

astro title="src/pages/index.astro"
<Layout title="Astro x SST">
  <main>
    <form action={url}>
      <input name="file" type="file" accept="image/png, image/jpeg" />
      <button type="submit">Upload</button>
    </form>
    <script>
      const form = document.querySelector("form");
      form!.addEventListener("submit", async (e) => {
        e.preventDefault();

        const file = form!.file.files?.[0]!;

        const image = await fetch(form!.action, {
          body: file,
          method: "PUT",
          headers: {
            "Content-Type": file.type,
            "Content-Disposition": `attachment; filename="${file.name}"`,
          },
        });

        window.location.href = image.url.split("?")[0] || "/";
      });
    </script>
  </main>
</Layout>

Add some styles, add this to your src/pages/index.astro.

astro title="src/pages/index.astro"
<style>
  main {
    margin: auto;
    padding: 1.5rem;
    max-width: 60ch;
  }
  form {
    color: white;
    padding: 2rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: #23262d;
    background-image: none;
    background-size: 400%;
    border-radius: 0.6rem;
    background-position: 100%;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  }
  button {
    appearance: none;
    border: 0;
    font-weight: 500;
    border-radius: 5px;
    font-size: 0.875rem;
    padding: 0.5rem 0.75rem;
    background-color: white;
    color: #111827;
  }
  button:active:enabled {
    background-color: #EEE;
  }
</style>

4. Generate a pre-signed URL

When our app loads, we'll generate a pre-signed URL for the file upload and use it in the form. Add this to the header on your src/pages/index.astro.

astro title="src/pages/index.astro" {8}
---
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const command = new PutObjectCommand({
  Key: crypto.randomUUID(),
  Bucket: Resource.MyBucket.name,
});
const url = await getSignedUrl(new S3Client({}), command);
---

:::tip
We are directly accessing our S3 bucket with Resource.MyBucket.name.
:::

And install the npm packages.

bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

Head over to the local Astro site in your browser, http://localhost:4321 and try uploading an image. You should see it upload and then download the image.


5. Deploy your app

Now let's deploy your app to AWS.

bash
npx sst deploy --stage production

You can use any stage name here but it's good to create a new stage for production.


Containers

We are going to create a Astro site, add an S3 Bucket for file uploads, and deploy it in a container with the Cluster component.

:::tip[View source]
You can view the source of this example in our repo.
:::

Before you get started, make sure to configure your AWS credentials.


1. Create a project

Let's start by creating our project.

bash
npm create astro@latest aws-astro-container
cd aws-astro-container

We are picking all the default options.


Init SST

Now let's initialize SST in our app.

bash
npx sst@latest init
npm install

Select the defaults and pick AWS. This'll create a sst.config.ts file in your project root.

It'll also ask you to update your astro.config.mjs. But we'll instead use the Node.js adapter since we're deploying it through a container.

bash
npx astro add node

2. Add a Service

To deploy our Astro site in a container, we'll use AWS Fargate with Amazon ECS. Replace the run function in your sst.config.ts.

ts title="sst.config.ts" {10-12}
async run() {
  const vpc = new sst.aws.Vpc("MyVpc");
  const cluster = new sst.aws.Cluster("MyCluster", { vpc });

  new sst.aws.Service("MyService", {
    cluster,
    loadBalancer: {
      ports: [{ listen: "80/http", forward: "4321/http" }],
    },
    dev: {
      command: "npm run dev",
    },
  });
}

This creates a VPC, and an ECS Cluster with a Fargate service in it.

:::note
By default, your service in not deployed when running in dev.
:::

The dev.command tells SST to instead run our Astro site locally in dev mode.


Start dev mode

Run the following to start dev mode. This'll start SST and your Astro site.

bash
npx sst dev

Once complete, click on MyService in the sidebar and open your Astro site in your browser.


3. Add an S3 Bucket

Let's allow public access to our S3 Bucket for file uploads. Update your sst.config.ts.

ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
  access: "public"
});

Add this below the Vpc component.


Now, link the bucket to the container.

ts title="sst.config.ts" {3}
new sst.aws.Service("MyService", {
  // ...
  link: [bucket],
});

This will allow us to reference the bucket in our Astro site.


4. Create an upload form

Add the upload form client in src/pages/index.astro. Replace the <Layout /> component with:

astro title="src/pages/index.astro"
<Layout title="Astro x SST">
  <main>
    <form action={url}>
      <input name="file" type="file" accept="image/png, image/jpeg" />
      <button type="submit">Upload</button>
    </form>
    <script>
      const form = document.querySelector("form");
      form!.addEventListener("submit", async (e) => {
        e.preventDefault();

        const file = form!.file.files?.[0]!;

        const image = await fetch(form!.action, {
          body: file,
          method: "PUT",
          headers: {
            "Content-Type": file.type,
            "Content-Disposition": `attachment; filename="${file.name}"`,
          },
        });

        window.location.href = image.url.split("?")[0] || "/";
      });
    </script>
  </main>
</Layout>

Add some styles, add this to your src/pages/index.astro.

astro title="src/pages/index.astro"
<style>
  main {
    margin: auto;
    padding: 1.5rem;
    max-width: 60ch;
  }
  form {
    color: white;
    padding: 2rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    background-color: #23262d;
    background-image: none;
    background-size: 400%;
    border-radius: 0.6rem;
    background-position: 100%;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  }
  button {
    appearance: none;
    border: 0;
    font-weight: 500;
    border-radius: 5px;
    font-size: 0.875rem;
    padding: 0.5rem 0.75rem;
    background-color: white;
    color: #111827;
  }
  button:active:enabled {
    background-color: #EEE;
  }
</style>

5. Generate a pre-signed URL

When our app loads, we'll generate a pre-signed URL for the file upload and use it in the form. Add this to the header on your src/pages/index.astro.

astro title="src/pages/index.astro" {8}
---
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

const command = new PutObjectCommand({
  Key: crypto.randomUUID(),
  Bucket: Resource.MyBucket.name,
});
const url = await getSignedUrl(new S3Client({}), command);
---

:::tip
We are directly accessing our S3 bucket with Resource.MyBucket.name.
:::

And install the npm packages.

bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

Head over to the local Astro site in your browser, http://localhost:4321 and try uploading an image. You should see it upload and then download the image.


6. Deploy your app

To deploy our app we'll add a Dockerfile.

dockerfile title="dockerfile"
FROM node:lts AS base
WORKDIR /app

COPY package.json package-lock.json ./

FROM base AS prod-deps
RUN npm install --omit=dev

FROM base AS build-deps
RUN npm install

FROM build-deps AS build
COPY . .
RUN npm run build

FROM base AS runtime
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist

ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321
CMD node ./dist/server/entry.mjs

:::tip
You need to be running Docker Desktop to deploy your app.
:::

Let's also add a .dockerignore file in the root.

bash title=".dockerignore"
.DS_Store
node_modules
dist

Now to build our Docker image and deploy we run:

bash
npx sst deploy --stage production

You can use any stage name here but it's good to create a new stage for production.

Congrats! Your app should now be live!


Connect the console

As a next step, you can setup the SST Console to git push to deploy your app and view logs from it.

You can create a free account and connect it to your AWS account.


Www Src Content Docs Docs Start Aws Auth (`www/src/content/docs/docs/start/aws/auth.mdx`)


title: OpenAuth with SST and Next.js

import { Image } from "astro:assets"

import nextApp from "../../../../../assets/docs/start/openauth-nextjs.png"
import consoleAutodeploy from "../../../../../assets/docs/start/sst-console-autodeploy.png"

We are going to create a new Next.js app, add authentication to it with OpenAuth, and deploy it with OpenNext and SST.

:::tip[View source]
You can view the source of this example in our repo.
:::

Before you get started, make sure to configure your AWS credentials.


Examples

We also have another OpenAuth example that you can refer to.


1. Create a project

Let's start by creating our Next.js app and starting it in dev mode.

bash
npx create-next-app@latest aws-auth-nextjs
cd aws-auth-nextjs

We are picking TypeScript and not selecting ESLint.


Init SST

Now let's initialize SST in our app.

bash
npx sst@latest init

Select the defaults and pick AWS. This'll create a sst.config.ts file in your project root.


2. Add OpenAuth server

Next, let's add a directory for our OpenAuth server.

bash
mkdir auth

Add our OpenAuth server to a auth/index.ts file.

ts title="auth/index.ts"
import { handle } from "hono/aws-lambda";
import { issuer } from "@openauthjs/openauth";
import { CodeUI } from "@openauthjs/openauth/ui/code";
import { CodeProvider } from "@openauthjs/openauth/provider/code";
import { subjects } from "./subjects";

async function getUser(email: string) {
  // Get user from database and return user ID
  return "123";
}

const app = issuer({
  subjects,
  // Remove after setting custom domain
  allow: async () => true,
  providers: {
    code: CodeProvider(
      CodeUI({
        sendCode: async (email, code) => {
          console.log(email, code);
        },
      }),
    ),
  },
  success: async (ctx, value) => {
    if (value.provider === "code") {
      return ctx.subject("user", {
        id: await getUser(value.claims.email),
      });
    }
    throw new Error("Invalid provider");
  },
});

export const handler = handle(app);

Define subjects

We are also going to define our subjects. Add the following to a auth/subjects.ts file.

ts title="auth/subjects.ts"
import { object, string } from "valibot";
import { createSubjects } from "@openauthjs/openauth/subject";

export const subjects = createSubjects({
  user: object({
    id: string(),
  }),
});

Let's install our dependencies.

bash
npm install @openauthjs/openauth valibot hono

Add Auth component

Now let's add this to our SST app. Replace the run function in sst.config.ts with the following.

ts title="sst.config.ts" {6}
const auth = new sst.aws.Auth("MyAuth", {
  issuer: "auth/index.handler",
});

new sst.aws.Nextjs("MyWeb", {
  link: [auth],
});

This is defining our OpenAuth component and linking it to our Next.js app.


Start dev mode

Run the following to start dev mode. This'll start SST, your Next.js app, and your OpenAuth server.

bash
npx sst dev

Once complete, it should give you the URL of your OpenAuth server.

bash
βœ“  Complete
   MyAuth: https://fv62a3niazbkrazxheevotace40affnk.lambda-url.us-east-1.on.aws

Also click on MyWeb in the sidebar and open your Next.js app by going to http://localhost:3000.


3. Add OpenAuth client

Next, let's add our OpenAuth client to our Next.js app. Add the following to app/auth.ts.

ts title="app/auth.ts" {7}
import { Resource } from "sst";
import { createClient } from "@openauthjs/openauth/client";
import { cookies as getCookies } from "next/headers";

export const client = createClient({
  clientID: "nextjs",
  issuer: Resource.MyAuth.url,
});

export async function setTokens(access: string, refresh: string) {
  const cookies = await getCookies();

  cookies.set({
    name: "access_token",
    value: access,
    httpOnly: true,
    sameSite: "lax",
    path: "/",
    maxAge: 34560000,
  });
  cookies.set({
    name: "refresh_token",
    value: refresh,
    httpOnly: true,
    sameSite: "lax",
    path: "/",
    maxAge: 34560000,
  });
}

Here we are linking to our auth server. And once the user is authenticated, we'll be saving their access and refresh tokens in http only cookies.


Add auth actions

Let's add the server actions that our Next.js app will need to authenticate users. Add the following to app/actions.ts.

text
/* Detailed source-code truncated for AI context efficiency. */

This is adding an auth action that checks if a user is authenticated, login that starts the OAuth flow, and logout that clears the session.


Add callback route

When the OpenAuth flow is complete, users will be redirected back to our Next.js app. Let's add a callback route to handle this in app/api/callback/route.ts.

ts title="app/api/callback/route.ts"
import { client, setTokens } from "../../auth";
import { type NextRequest, NextResponse } from "next/server";

export async function GET(req: NextRequest) {
  const url = new URL(req.url);
  const code = url.searchParams.get("code");

  const exchanged = await client.exchange(code!, `${url.origin}/api/callback`);

  if (exchanged.err) return NextResponse.json(exchanged.err, { status: 400 });

  await setTokens(exchanged.tokens.access, exchanged.tokens.refresh);

  return NextResponse.redirect(`${url.origin}/`);
}

Once the user is authenticated, we redirect them to the root of our app.


4. Add auth to app

Now we are ready to add authentication to our app. Replace the <Home /> component in app/page.tsx with the following.

tsx title="app/page.tsx"
import { auth, login, logout } from "./actions";

export default async function Home() {
  const subject = await auth();

  return (
    <div className={styles.page}>
      <main className={styles.main}>
        <Image
          className={styles.logo}
          src="/next.svg"
          alt="Next.js logo"
          width={180}
          height={38}
          priority
        />
        <ol>
          {subject ? (
            <>
              <li>
                Logged in as <code>{subject.properties.id}</code>.
              </li>
              <li>
                And then check out <code>app/page.tsx</code>.
              </li>
            </>
          ) : (
            <>
              <li>Login with your email and password.</li>
              <li>
                And then check out <code>app/page.tsx</code>.
              </li>
            </>
          )}
        </ol>

        <div className={styles.ctas}>
          {subject ? (
            <form action={logout}>
              <button className={styles.secondary}>Logout</button>
            </form>
          ) : (
            <form action={login}>
              <button className={styles.primary}>Login with OpenAuth</button>
            </form>
          )}
        </div>
      </main>
    </div>
  );
}

Let's also add these styles to app/page.module.css.

css title="app/page.module.css"
.ctas button {
  appearance: none;
  background: transparent;
  border-radius: 128px;
  height: 48px;
  padding: 0 20px;
  border: none;
  border: 1px solid transparent;
  transition:
    background 0.2s,
    color 0.2s,
    border-color 0.2s;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  line-height: 20px;
  font-weight: 500;
}

button.primary {
  background: var(--foreground);
  color: var(--background);
  gap: 8px;
}

button.secondary {
  border-color: var(--gray-alpha-200);
  min-width: 180px;
}

Test your app

Head to http://localhost:3000 and click the login button, you should be redirected to the OpenAuth server asking you to put in your email.

If you check the Functions tab in your sst dev session, you'll see the code being console logged. You can use this code to login.

This should log you in and print your user ID.


5. Deploy your app

Now let's deploy your app to AWS.

bash
npx sst deploy --stage production

You can use any stage name here but it's good to create a new stage for production.

bash
βœ“  Complete
   MyAuth: https://vp3honbl3od4gmo7mei37mchky0waxew.lambda-url.us-east-1.on.aws
   MyWeb: https://d2fjg1rqbqi95t.cloudfront.net

Congrats! Your app and your OpenAuth server should now be live!


Connect the console

As a next step, you can setup the SST Console to git push to deploy your app and view logs from it.

You can create a free account and connect it to your AWS account.


Www Src Content Docs Docs Start Aws Bun (`www/src/content/docs/docs/start/aws/bun.mdx`)


title: Bun on AWS with SST

We are going to build an app with Bun, add an S3 Bucket for file uploads, and deploy it to AWS in a container with SST.

:::tip[View source]
You can view the source of this example in our repo.
:::

Before you get started, make sure to configure your AWS credentials.


Examples

We also have a few other Bun examples that you can refer to.


1. Create a project

Let's start by creating our Bun app.

bash
mkdir aws-bun && cd aws-bun
bun init -y

Init Bun Serve

Replace your index.ts with the following.

js title="index.ts