# Technical Documentation: anomalyco/sst

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/anomalyco/sst)

### Overview (README.md)

<p align="center">
  <a href="https://sst.dev/">
    <img alt="SST" src="https://raw.githubusercontent.com/sst/identity/main/variants/sst-full.svg" width="300" />
  </a>
</p>

<p align="center">
  <a href="https://sst.dev/discord"><img alt="Discord" src="https://img.shields.io/discord/983865673656705025?style=flat-square&label=Discord" /></a>
  <a href="https://www.npmjs.com/package/sst"><img alt="npm" src="https://img.shields.io/npm/v/sst.svg?style=flat-square" /></a>
  <a href="https://github.com/anomalyco/sst/actions/workflows/build.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/anomalyco/sst/build.yml?style=flat-square&branch=dev" /></a>
</p>

---

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](https://sst.dev/docs/reference/cli/).

#### Manually

Download the pre-compiled binaries from the [releases](https://github.com/sst/sst/releases/latest) page and copy to the desired location.

## Get Started

Get started with your favorite framework:

- [Next.js](https://sst.dev/docs/start/aws/nextjs)
- [Remix](https://sst.dev/docs/start/aws/remix)
- [Astro](https://sst.dev/docs/start/aws/astro)
- [API](https://sst.dev/docs/start/aws/api)

## Learn More

Learn more about some of the key concepts:

- [Live](https://sst.dev/docs/live)
- [Linking](https://sst.dev/docs/linking)
- [Console](https://sst.dev/docs/console)
- [Components](https://sst.dev/docs/components)

## 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](https://go.dev/) and [Bun](https://bun.sh/) 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](https://sst.dev/discord) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/SST_dev)

---

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

---
title: Blog
template: splash
description: News and updates from the SST team.
pagefind: false
---

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

<PostList />

---

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

---
title: What is SST
description: Build full-stack apps on your own infrastructure.
---

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](/docs/all-providers#directory).
:::

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.

<Tabs>
  <TabItem label="Next.js">
  ```ts title="sst.config.ts"
  new sst.aws.Nextjs("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="Remix">
  ```ts title="sst.config.ts"
  new sst.aws.Remix("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="Astro">
  ```ts title="sst.config.ts"
  new sst.aws.Astro("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="Svelte">
  ```ts title="sst.config.ts"
  new sst.aws.SvelteKit("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="Solid">
  ```ts title="sst.config.ts"
  new sst.aws.SolidStart("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

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.

<Tabs>
  <TabItem label="Containers">
  ```ts title="sst.config.ts"
  const cluster = new sst.aws.Cluster("MyCluster", { vpc });

  new sst.aws.Service("MyService", {
    cluster,
    loadBalancer: {
      ports: [{ listen: "80/http" }]
    }
  });
  ```
  </TabItem>
  <TabItem label="Functions">
  ```ts title="sst.config.ts"
  new sst.aws.Function("MyFunction", {
    handler: "src/lambda.handler"
  });
  ```
  </TabItem>
  <TabItem label="Postgres">
  ```ts title="sst.config.ts"
  new sst.aws.Postgres("MyDatabase", { vpc });
  ```
  </TabItem>
  <TabItem label="Bucket">
  ```ts title="sst.config.ts"
  new sst.aws.Bucket("MyBucket");
  ```
  </TabItem>
  <TabItem label="Cron">
  ```ts title="sst.config.ts"
  new sst.aws.Cron("MyCronJob", {
    job: "src/cron.handler",
    schedule: "rate(1 minute)"
  });
  ```
  </TabItem>
</Tabs>


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](/docs/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](/docs/components#transforms).

---

### Providers

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

<VideoAside title="Watch a video about providers in SST" href="https://youtu.be/rlR2f5N9mW4" />

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](/docs/providers) and check out the full list in the [Directory](/docs/all-providers#directory).

---

## Link resources

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.

<VideoAside title="Watch a video on linking resources" href="https://youtu.be/s8cWklU4Akw" />

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](/docs/reference/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](/docs/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.

<VideoAside title="Watch a video about SST's project structure" href="https://youtu.be/mserRA-CWRw" />

---

### 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</a> 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](/docs/set-up-a-monorepo/).

---

## CLI

To make this all work, SST comes with a [CLI](/docs/reference/cli/). For JavaScript projects, install SST locally in your project so the CLI version is tracked with your app.

<Tabs syncKey="package-manager">
  <TabItem label="npm">
  ```bash
  npm install sst
  ```
  </TabItem>
  <TabItem label="pnpm">
  ```bash
  pnpm add sst
  ```
  </TabItem>
  <TabItem label="Bun">
  ```bash
  bun add sst
  ```
  </TabItem>
  <TabItem label="Yarn">
  ```bash
  yarn add sst
  ```
  </TabItem>
</Tabs>

You can then run the CLI with your package manager.

<Tabs syncKey="package-manager">
  <TabItem label="npm">
  ```bash
  npx sst dev
  ```
  </TabItem>
  <TabItem label="pnpm">
  ```bash
  pnpm exec sst dev
  ```
  </TabItem>
  <TabItem label="Bun">
  ```bash
  bun sst dev
  ```
  </TabItem>
  <TabItem label="Yarn">
  ```bash
  yarn sst dev
  ```
  </TabItem>
</Tabs>

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](/docs/reference/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_](/docs/live/), letting you make and test changes without having to redeploy them.
3. Creates a [_tunnel_](/docs/reference/cli#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.

<VideoAside title="Watch a video about dev mode" href="https://youtu.be/mefLc137EB0" />

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`](/docs/reference/cli/#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](/docs/reference/cli#stage).

---

## Console

Once you are ready to go to production, you can use the [SST Console](/docs/console/) to **auto-deploy** your app, create **preview environments**, and **monitor** for any issues.



Learn more about the [Console](/docs/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`](/docs/component/aws/nextjs/) or [`Email`](/docs/component/aws/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_](/docs/live/), creates a [_tunnel_](/docs/reference/cli#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](/docs/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](/docs/basics/)
2. Create your first SST app
   - [Build a Next.js app in AWS](/docs/start/aws/nextjs/)
   - [Deploy Bun in a container to AWS](/docs/start/aws/bun/)
   - [Build a Hono API with Cloudflare Workers](/docs/start/cloudflare/hono/)

---

### 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
description: Learn about the team behind SST.
pagefind: false
hero:
  title: "n/a"
---

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

<About />

---

### 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
description: The first-ever SST conference!
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](https://github.com/sst/sst/releases/tag/v1.0.0-beta.26). 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**](https://v1conf.sst.dev) is a free virtual event happening on May 17th, 2022 at 9am PDT. [Register for the conference now](https://v1conf.sst.dev).

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](https://forms.gle/KTdbCLUy5oNwos2m7) 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](https://v1conf.sst.dev) 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](https://v1conf.sst.dev).

_Psst!_ Check out the domain [the conference website](https://v1conf.sst.dev) 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
description: We are adding a new component for Amazon Aurora Serverless v2.
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`](/docs/component/aws/aurora), a new component for [Amazon Aurora Serverless v2](https://aws.amazon.com/rds/aurora/serverless/). Recently, AWS announced that Aurora Serverless v2 can [scale to 0](https://aws.amazon.com/blogs/database/introducing-scaling-to-0-capacity-with-amazon-aurora-serverless-v2/) and auto-pause. This is good for dev or PR stages.

There are some differences between this and the [`Postgres`](/docs/component/aws/postgres) RDS component. We talk about it here in this video.

<YouTube id="xt2PoZDcwxY" posterQuality="high" />

---

## 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`](/docs/component/aws/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`](/docs/component/aws/aurora#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`](/docs/component/aws/aurora#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](/docs/component/aws/aurora#cost).

---

## Examples

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

- [Aurora Postgres](/docs/examples/#aws-aurora-postgres)
- [Aurora MySQL](/docs/examples/#aws-aurora-mysql)
- [Aurora local](/docs/examples/#aws-aurora-local)

---

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

---
template: splash
title: Auth
description: We are launching a modern lightweight authentication library for your SST apps.
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](https://www.youtube.com/watch?v=cO9Chk6sUW4) where we did a demo and a deep dive into the internals.

<YouTube id="cO9Chk6sUW4" posterQuality="high" />

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
description: We are talking to Michael Orcutt, the founder of Buildforce about their experience building with SST and Seed.
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](https://buildforce.com) about their experience building with [SST](/) and [Seed](https://seed.run).

### About Buildforce

[Buildforce](https://buildforce.com) 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](https://seed.run) for their deployment workflow. They typically branch from main, work locally, push to a feature branch. The feature branches get deployed through [Seed](https://seed.run) 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](https://joinbuildforce.recruitee.com).

---

[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
description: We are launching a set of tools to securely manage secrets and environment variables in your SST apps.
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**](/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](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) Parameters in your AWS account. They are stored with the _Standard Parameter type_ and _Standard Throughput_.

This makes Config [**free to use**](https://aws.amazon.com/systems-manager/pricing/) in your SST apps.

## Launch event

We hosted a [launch livestream on YouTube](https://www.youtube.com/watch?v=6sMTfoeshLo) where we did a deep dive of the Config and its internals.

<YouTube id="6sMTfoeshLo" posterQuality="high" />

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
   1. Parameter vs Lambda environment variables
   1. Deep dive into the Secrets code
   1. IAM permission for fetching secrets
   1. CLI command `sst secrets`
   1. Secrets fallback
4. Q&A
   1. Q: What is the AWS cost of using Config?
   1. Q: What does the SSM path look like?
   1. Q: Managing secrets in my CI pipeline
   1. Q: Managing secrets across AWS accounts
   1. Q: Accessing Config inside or outside the handler
   1. Q: Would changing a secret require redeployment?
   1. Q: Using Config for tests
   1. Q: SSM vs Secret Manager
   1. Q: Export secrets to a .env file
   1. 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**](/docs/).

---

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

---
template: splash
title: Configure Autodeploy workflow
description: You can now completely control the build process when the Console auto-deploys your app.
author: jay
pagefind: false
lastUpdated: 2025-01-30
---

You can now completely control the build process when auto-deploying your app through the [Console](/docs/console).

##### Background

After you've enabled [Autodeploy](/docs/console#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`**](/docs/reference/config#console-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](https://bun.sh/docs/runtime/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](https://github.com/sst/sst/blob/dev/www/sst.config.ts); 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](/docs/reference/config#console-autodeploy-workflow) 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`](/docs/reference/config#console-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](/docs/console) and [Autodeploy](/docs/console#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
description: Updates to the Console to better support container applications.
author: jay
lastUpdated: 2024-11-25
pagefind: false
---

We are rolling out some updates to the [Console](/docs/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`](/docs/reference/config#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`](/docs/reference/config#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`](/docs/reference/config#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](/docs/console#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
description: We are rolling out an update to the pricing of the Console.
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](#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](/docs/console#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](/docs/console#autodeploy) your apps. And added broader support for [container services](/blog/container-support). 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](/docs/console#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](https://github.com/sst/console) 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][contact-us] and we can figure out a pricing plan that works for you.

[**Learn more about the new pricing in our docs**](/docs/console#pricing).

---

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


[contact-us]: mailto:hello@sst.dev

---

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

---
template: splash
title: Container Spot capacity
description: We are adding support for using the Fargate Spot capacity provider for services.
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`](/docs/component/aws/cluster) component through the [`capacity`](/docs/component/aws/cluster#capacity) prop. Spot instances can be around 50% cheaper and we talk about how they work here.

<YouTube id="2RrAZiTZoeA" posterQuality="high" />

## Background

You can create container services with ECS and Fargate in SST with the [`Cluster`](/docs/component/aws/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`](/docs/component/aws/cluster#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`**](/docs/component/aws/cluster#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**](/docs/examples/#aws-cluster-spot-capacity).

You can also check out our container service quick starts.

- [Bun](/docs/start/aws/bun)
- [Deno](/docs/start/aws/deno)
- [NestJS](/docs/start/aws/nestjs)
- [Express](/docs/start/aws/express)

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
description: SST now natively supports building containerized applications.
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.

<YouTube id="sg4CnvoI7eg" posterQuality="high" />

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`](/docs/component/aws/cluster) & [`Service`](/docs/component/aws/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**](https://x.com/jayair/status/1853848336538673606) for your applications.

- [`Vpc`](/docs/component/aws/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`](/docs/component/aws/postgres), [`Redis`](/docs/component/aws/redis), & [`Efs`](/docs/component/aws/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](/docs/component/aws/vpc#cost).

You can [read more about what we've done here](https://x.com/jayair/status/1851019182122652125).

---

### 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`](/docs/reference/cli#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](https://x.com/jayair/status/1844055259729007084).

---

### 3. Console

The [SST Console](/docs/console) now shows you logs for your containers. And [Autodeploy](/docs/console#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.

- [Bun](/docs/start/aws/bun)
- [Nuxt](/docs/start/aws/nuxt)
- [Solid](/docs/start/aws/solid)
- [Deno](/docs/start/aws/deno)
- [Hono](/docs/start/aws/hono)
- [Astro](/docs/start/aws/astro)
- [Remix](/docs/start/aws/remix)
- [Svelte](/docs/start/aws/svelte)
- [Next.js](/docs/start/aws/nextjs)
- [Drizzle](/docs/start/aws/drizzle)
- [Prisma](/docs/start/aws/prisma)
- [Express](/docs/start/aws/express)

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
description: We are launching a CLI that helps you get started with SST easily.
lastUpdated: 2022-06-28
pagefind: false
---

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

Today we are launching [`create sst`](https://github.com/sst/sst/tree/master/packages/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](https://12factor.net) 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)
  - [Kysely](https://github.com/koskimas/kysely) for SQL
  - [ElectroDB](https://github.com/tywalch/electrodb) for DynamoDB
- GraphQL as the API
  - [Pothos](https://pothos-graphql.dev) 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](https://www.youtube.com/watch?v=wBTDkLIyMhw).

<YouTube id="wBTDkLIyMhw" posterQuality="high" />

Here's roughly what we covered during the launch:

- Backstory
- Demo of `create sst`
- [Tyler W. Walch](https://twitter.com/tinkertamper) from [ElectroDB](https://github.com/tywalch/electrodb)
- [Michael Hayes](https://twitter.com/yavascript) from [Pothos](https://pothos-graphql.dev)
- Looking ahead
- Q&A

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
description: In this post we are talking to Orlando Hui from Doorvest about how they are using SST and Seed to simplify real estate investing.
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](https://doorvest.com) about how they are using [SST](/) and [Seed](https://seed.run) to simplify real estate investing.


### About Doorvest

[Doorvest](https://doorvest.com) 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](https://seed.run), 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](https://www.builtinsf.com/company/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
description: Modern serverless frontends are hard to deploy and here's why.
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](https://www.netlify.com/) and [Vercel](https://vercel.com/) that have built [custom infrastructure](https://vercel.com/blog/framework-defined-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.

<SpaDiagram style="width: min(100%, 480px);" />

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.

<SsrDiagram style="width: min(100%, 480px);" />

There are also a dozen or so competing frameworks.

<MarketMap style="width: min(100%, 480px);" />

This is complicated enough that even AWS and GCP's services like [Amplify](https://aws.amazon.com/amplify/) and [Firebase](https://firebase.google.com/) 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](https://opennext.js.org) 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](https://aws.amazon.com/about-aws/whats-new/2024/11/amazon-cloudfront-origin-modifications-cloudfront-functions/).

<picture style="width: min(100%, 480px);">
  <source srcset={tweetDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={tweetLight.src} media="(prefers-color-scheme: light)" />
  <Image src={tweetLight} alt="CloudFront announcement tweet" />
</picture>

---

## 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`](/docs/component/aws/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:

- [`Router`](/docs/component/aws/router/) component docs
- Guide on [**configuring a router**](/docs/configure-a-router)
- The [`router`](/docs/component/aws/nextjs/#router) prop in your frontend
- Multiple [`regions`](/docs/component/aws/nextjs/#regions) in your frontend

---

## 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
description: SST v3 now supports the Golang runtime for Lambda functions.
author: jay
pagefind: false
lastUpdated: 2024-12-20
---

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

SST v3 now supports the [`go`](/docs/component/aws/function#runtime) 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:

<YouTube id="WTAeW0wyTzA" posterQuality="high" />

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**](/docs/examples/#aws-lambda-go).

---

Also in this update:

1. The [`sst dev`](/docs/reference/cli/#dev) CLI supports running your Go functions [_Live_](/docs/live/).

2. You can use our new [Go SDK](/docs/reference/sdk/#golang) 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](/docs/reference/sdk/#clients) that are available in the JS SDK.

3. You can view Go function logs in the [Console](/docs/console/). Support for [Issues](/docs/console/#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
description: In this post we are talking to Jack Fraser, a Software Engineering Manager at Henry Schein One; the world's largest dental practice management software company.
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](https://henryscheinone.com). They are using [SST](/) and [Seed](https://seed.run) to power the patient booking experience and the management software used by thousands of dental practices globally.

## About Henry Schein One

[Henry Schein One](https://henryscheinone.com) 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](https://seed.run). _"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](https://dentr.co.uk/jobs) 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
description: We are launching a new livestream series showcasing the next generation of serverless tools and creators.
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.

[](https://isserverlessready.com)

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**](https://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
description: Issues now reports errors from container applications.
author: jay
lastUpdated: 2025-03-04
pagefind: false
---

import { Image } from "astro:assets"

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

[Issues](/docs/console#issues) now reports errors from Node.js container applications. Previously, only errors from Lambda functions were reported.

<Image src={issues} alt="SST Console Container Issues" />

---

### 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](/docs/reference/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](/docs/console#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
description: In this post we are talking to Ross Coundon, CTO of Leadent Digital about how they are using SST to transform field service operations.
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](https://leadent.digital) about how they are using [SST](/) and [Seed](https://seed.run) to transform field service operations.

### About Leadent Digital

[Leadent Digital](https://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](https://seed.run) 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](https://leadent.digital).

---

[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
description: We have a brand new tutorial that teaches you the basics of 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](https://www.youtube.com/watch?v=i7xEKHWTKNk) 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.

<YouTube id="i7xEKHWTKNk" posterQuality="high" />

---

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
description: We launched a new construct that makes it easy to run functions longer than 15 minutes.
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](https://www.youtube.com/watch?v=7sYdSbmi-ik) where we demoed the new construct, did a deep dive, and answered some questions.

<YouTube id="7sYdSbmi-ik" posterQuality="high" />

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
description: We are working on a new version of SST that’s not based on 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](https://github.com/sst/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](https://movies.sst.dev)

[](https://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](#what-is-ion)
2. [What’s Wrong With CDK & CFN?](#whats-wrong-with-cdk--cfn)
   - [Design Flaws](#design-flaws)
     1. [CFN Is a Black Box](#1-cfn-is-a-black-box)
     2. [CDK Is Not IaC, It’s a CFN Hack](#2-cdk-is-not-iac-its-a-cfn-hack)
   - [Practical Problems](#practical-problems)
     1. [Linking Resources](#1-linking-resources)
     2. [Cyclical Dependencies](#2-cyclical-dependencies)
     3. [Export in Use](#3-export-in-use)
     4. [Swallowing Errors](#4-swallowing-errors)
     5. [Custom Resource Hacks](#5-custom-resource-hacks)
     6. [Rollback Hell](#6-rollback-hell)
     7. [Double Builds](#7-double-builds)
     8. [Stack Resource Limits](#8-stack-resource-limits)
     9. [Orphan Stacks](#9-orphan-stacks)
     10. [Importing Resources](#10-importing-resources)
     11. [Slowness](#11-slowness)
3. [Why The Change](#why-the-change)
   - [Background](#background)
   - [Supporting Non-AWS Providers](#supporting-non-aws-providers)
   - [What Changed](#what-changed)
   - [Working With Pulumi](#working-with-pulumi)
4. [How Does Ion Work](#how-does-ion-work)
5. [Roadmap](#roadmap)
   - [Step 0: Ion Prototype Demo](#step-0-ion-prototype-demo)
   - [Step 1: Ion General Release](#step-1-ion-general-release)
   - [Step 2: Ion → SST v3](#step-2-ion--sst-v3)
6. [What About SST v2](#what-about-sst-v2)
7. [What About Migrating](#what-about-migrating)
   - [Is Ion Right For Me](#is-ion-right-for-me)
   - [What Ion Is Not](#what-ion-is-not)
8. [What’s Our Goal](#whats-our-goal)
9. [FAQ](#faq)

We also covered this post on a livestream.

<YouTube id="4jphEjFIys8" posterQuality="high" />

---

## What Is Ion?

Ion is a code name for a new engine for deploying SST applications. The constructs (or components) are defined using [Terraform](https://www.terraform.io) providers and deployed using [Pulumi](https://www.pulumi.com); 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](https://v2.sst.dev/config). 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](https://v2.sst.dev/live-lambda-development) & [Resource Binding](https://v2.sst.dev/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](https://seed.run) — 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](https://www.serverless.com) apps. We've seen the ups and downs of Serverless Framework and their misstep with the [Serverless Components](https://github.com/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](https://open-next.js.org). 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.

[<img src={pulumiTweet.src} alt="SST Pulumi tweet" style="width: min(100%, 360px);"/>](https://twitter.com/funcOfJoe/status/1690115160877535232)

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](https://github.com/sst/ion)
- 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](https://movies.sst.dev)

You can view the repo for our demo movies app on [GitHub](https://github.com/sst/demo-ai-app). 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
description: We are moving our Slack community to Discord!
author: jay
lastUpdated: 2022-06-23
pagefind: false
---

We are moving our Slack community to [Discord](https://discord.gg/sst)! 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**](https://discord.gg/sst)

If you are curious about why we are moving to Discord, I [shared some thoughts on this on Twitter](https://x.com/jayair/status/1535360228631379973).

We hope to see you in [Discord](https://discord.gg/sst) 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
description: We've updated how you view your application logs in the SST Console.
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**.

<picture>
  <source srcset={logsDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={logsLight.src} media="(prefers-color-scheme: light)" />
  <Image src={logsLight} alt="SST Console Logs" />
</picture>

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](/docs/console#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
description: The new SST Console is out.
pagefind: false
---

The new SST Console is out. You can [learn more](/docs/console/) 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**](https://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](https://seed.run), and you can [view the source on GitHub](https://github.com/sst/console).

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](/docs/console/#pricing).

### Old Console

The old SST Console is still accessible at [old.console.sst.dev](https://old.console.sst.dev). But we'll be moving away from it in the future.

### Learn more

[Learn more about the new SST Console](/docs/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
description: OpenNext is an open-source Next.js serverless adapter created by SST.
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](https://open-next.js.org) 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.

<YouTube id="k_8lBdPC3gk" posterQuality="high" />

## 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](https://github.com/sst/open-next/releases/tag/v1.0.0) 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**](/docs/start/aws/nextjs/). If you are using a different framework, check out the [other deployment options](https://github.com/sst/open-next/blob/main/README.md#deployment).

---

We could use your support in maintaining OpenNext, make sure to join `#open-next` on Discord and [star us on GitHub](https://github.com/sst/open-next).

---

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

---
template: splash
title: OpenAuth Beta
description: OpenAuth and the new Auth component is now in 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](https://openauth.js.org/)

A universal, standards-based auth provider. It's in beta along with the new [`Auth`](/docs/component/aws/auth) component for SST v3 that uses this.

<YouTube id="mKKx8uXw5ak" posterQuality="high" />

---

## What is OpenAuth

[OpenAuth](https://openauth.js.org/) 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.

<YouTube id="SSjNUuQ06tk" posterQuality="high" />

You can learn more about it over on the [OpenAuth GitHub](https://github.com/toolbeam/openauth).

---

## 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`](/docs/component/aws/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](https://github.com/toolbeam/openauth) and check out the examples.

You can [star the repo](https://github.com/toolbeam/openauth) and [follow us on X](https://x.com/SST_dev) 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
description: OpenNext is now a broader community effort.
author: jay
lastUpdated: 2024-09-24
pagefind: false
---

We launched [OpenNext](https://opennext.js.org) back in April 2023 to build an open-source adapter for Next.js.

Thanks to our community, most notable [conico974](https://github.com/conico974) and [khuezy](https://github.com/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:

- Following OpenNext on GitHub: [github.com/opennextjs](https://github.com/opennextjs)
- Joining the new Discord server: [discord.gg/opennextjs](https://discord.gg/opennextjs)

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
description: The new RemixSite construct makes it easy to deploy Remix apps to AWS.
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](https://remix.run) 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](https://twitter.com/controlplusb) put together a very comprehensive PR implementing an SST construct that would deploy a Remix app.

[Frank](https://twitter.com/fanjiewang) 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](https://www.youtube.com/watch?v=ZBbRZTTCwvU) where we did a deep dive of the construct and its features.

<YouTube id="ZBbRZTTCwvU" posterQuality="high" />

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](/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
description: Today we are announcing our $1M seed round.
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](https://techcrunch.com/2021/07/23/serverless-stack-raises-1m-for-open-source-application-framework/) 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](https://youtu.be/2w4A06IsBlU), 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](/docs/live/); our team is committed to adding features to support their workflow. This commitment shows clearly in [our rapid release history](https://github.com/sst/sst/releases).

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](https://seed.run); 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
description: We are officially changing our name from Serverless Stack to SST.
pagefind: false
---

We are now officially changing our name from Serverless Stack to SST. We are also moving our domain from [serverless-stack.com](https://serverless-stack.com) to [sst.dev](https://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**](https://sst.dev)!

---

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

---
template: splash
title: SST v1
description: SST officially hits 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](https://v1conf.sst.dev) as we celebrate its launch!

<YouTube id="hru_qLt2-ug" posterQuality="high" />

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](/docs/console/) 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.

<YouTube id="pfMeaPyPydo" posterQuality="high" />

### 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.

<YouTube id="cqzgAJvUQCg" posterQuality="high" />

### SST Console

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

<YouTube id="QvNMM5hpO3M" posterQuality="high" />

### Live Lambda Dev

When you run `sst start`, it’ll start up the [Live Lambda Development](/docs/live) environment. This allows you to set breakpoints and test your Lambda functions locally.

<YouTube id="2w4A06IsBlU" posterQuality="high" />

## SST 1.0 Conf

To commemorate SST hitting v1 we are holding the [**SST 1.0 Conf**](https://v1conf.sst.dev). 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](https://v1conf.sst.dev). So stay tuned!

---

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

---
title: SST v2 Preview
description: As the first episode of our new series, we are showing off a preview of SST's upcoming v2 release.
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?](https://isserverlessready.com) series, we are showing off a preview of SST's upcoming v2 release.

You can check it out on YouTube here.

<YouTube id="qEKUNZWbj-8" posterQuality="high" />

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`.
1. **New config** — There's a new config file. A `.js` file instead of the `.json`. Similar to what Vite has.
1. **New functions API** — We've refactored the props in the SST Functions construct.
1. **Improved codegen** — We've reworked how SST codegens your types.
1. **pnpm support** — We've made SST compatible with [pnpm](https://pnpm.io). We've internally migrated to pnpm as well!
1. **New CLI UI** — There's an all new CLI UI, that makes it easier to figure out what the CLI is doing.
1. **Faster CLI** — We've refactored the entire CLI codebase and optimized for speed. It's a lot faster than before.
1. **Faster Live Lambda Dev** — We've also adopted a new debug stack infrastructure, so [Live Lambda Dev](/docs/live/) is now way faster than before!
1. **Building modern frontends** — We are also going to be introducing new constructs for [Astro](https://astro.build) and [Solid](https://www.solidjs.com).
1. **OpenNext** — We also launched a new open source initiative called [OpenNext](https://open-next.js.org) 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
description: "SST v2 is ready. Here's what's new and how you can learn more about it."
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](https://youtu.be/v97-SJY1Mb0) to get a quick sense of what SST v2 is all about.

<YouTube id="v97-SJY1Mb0" posterQuality="high" />

Adam and Dax also did a whole [podcast on SST v2](https://tomorrow.fm/8) and the rewrite process.

We also did a [launch livestream](https://www.youtube.com/watch?v=v6oqlnY6-Vc) 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.

<YouTube id="v6oqlnY6-Vc" posterQuality="high" />

---

## 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`**](https://www.npmjs.com/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](/docs/live/) 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](/docs/live/).

- **`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](https://open-next.js.org) project.

  We also added a `AstroSite` construct for [Astro](https://astro.build) and the `SolidStartSite` construct for [Solid](https://www.solidjs.com).

- **pnpm support**

  SST now natively supports [pnpm](https://pnpm.io)! 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.

  <YouTube id="qEKUNZWbj-8" posterQuality="high" />

---

[**Seed**](https://seed.run) also [supports SST v2](https://seed.run/blog/sst-v2-support). 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
description: Ion is now officially 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](/blog/moving-away-from-cdk) 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**](/blog/moving-away-from-cdk).

Late last year we started work on a new deployment engine that would use [Pulumi](https://www.pulumi.com) and [Terraform](https://www.terraform.io), 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](https://ion.sst.dev) is now just [sst.dev](https://sst.dev). You might've already noticed the new design.

The docs, [sst.dev/docs](/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**](/docs/all-providers#directory).

3. `sst.config.ts`

   The structure of the SST config has changed a little.

   <Tabs>
     <TabItem label="v3">
     ```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() { }
     });
     ```
     </TabItem>
     <TabItem label="v2">
     ```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;
     ```
     </TabItem>
   </Tabs>

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](/docs/migrate-from-v2#major-changes) 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](/docs/) and [Basics](/docs/basics/) docs. These give you a good overview and lead you to other docs that expand on the various concepts.

- **Guide**

  Our [SST Guide](https://guide.sst.dev) 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](https://www.terminal.shop) was built with v3. It's comprehensive and you can [view the repo on GitHub](https://github.com/terminaldotshop/terminal) as well.

  <YouTube id="POlZS8PcyZw" posterQuality="high" />

---

## Migration

We've created a [**detailed migration guide**](/docs/migrate-from-v2) 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](/docs/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**](/docs/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](https://seed.run) 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](https://v2.sst.dev).

---

We know this is a big change and you might have some concerns about it. Feel free to <a href={config.discord}>join us on Discord</a> 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**</a> 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
description: We are adding a new component that allows you to run asynchronous tasks.
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`](/docs/component/aws/cluster#tasks), 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.

<YouTube id="3PJM7mtF-eo" posterQuality="high" />

## 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`](/docs/component/aws/cluster#tasks) 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**](/docs/examples/#aws-task) 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](/docs/component/aws/task#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](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_RunTask.html).

2. **Cron jobs**

   You can also connect your task to a [`Cron`](/docs/component/aws/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_](/docs/live/).

<Image src={cliImage} alt="SST Dev CLI Tasks tab" />

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](/docs/console/) supports viewing logs for your tasks when they are in production.

<Image src={consoleImage} alt="SST Console Task logs" />

---

## 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.

- [Adding a task](/docs/component/aws/cluster/#tasks)
- [Dev mode](/docs/component/aws/cluster/#dev-1)
- [JS SDK](/docs/component/aws/task/#sdk)
- [Cost](/docs/component/aws/cluster/#cost)

And check out these examples.

- [Invoking a task with a function](/docs/examples/#aws-task)
- [Invoking a task with a cron job](/docs/examples/#aws-task-cron)

---

#### Comparison to v2

If you are coming from SST v2, there are a couple of differences between `Task` and [`Job`](https://v2.sst.dev/constructs/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
description: You can write tests for your SST apps using a new CLI that auto-loads the secrets and config.
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](https://vitest.dev).

```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](https://www.youtube.com/watch?v=YtaxDURRjHA) where we talked about how to write tests for your SST apps.

<YouTube id="YtaxDURRjHA" posterQuality="high" />

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**](/docs/).

---

### Www Src Content Docs Blog Update Permalinks (`www/src/content/docs/blog/update-permalinks.mdx`)

---
template: splash
title: Update Permalinks
description: Every update made to an SST app now gets a unique URL.
author: jay
lastUpdated: 2025-02-18
pagefind: false
---

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**](https://x.com/thdxr/status/1891595772937863283) of this in action.

<picture>
  <source srcset={updatesDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={updatesLight.src} media="(prefers-color-scheme: light)" />
  <Image src={updatesLight} alt="SST Console Updates" />
</picture>

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](/docs/state) 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](/docs/console/#updates) in the Console and how [state](/docs/state) works over on the [docs](/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
description: SST v3 is now supported natively on Windows.
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](https://github.com/sst/sst/releases/tag/v3.14.0), native Windows support is now in beta.

The main difference between Windows and Linux/macOS is that [`sst dev`](/docs/reference/cli#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](https://github.com/sst/sst/issues/new).

---

### 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
description: Watch the recording of SST 1.0 Conf, our first-ever conference. 
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](https://v1conf.sst.dev) 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**](https://youtu.be/6FzLjpMYcu8).

<YouTube id="6FzLjpMYcu8" posterQuality="high" />

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](https://youtu.be/6FzLjpMYcu8?t=5182).

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**](https://forms.gle/HwkDsMnsPEhzxbaPA).

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
description: Use 150+ Pulumi or Terraform providers in your app.
---

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

Aside from the [built-in](/docs/components#built-in) components, SST supports any of the **150+** Pulumi and Terraform providers.

Check out the full list in the [Directory](#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](/docs/providers).

---

### Preloaded

SST comes preloaded with the following providers, so you **don't need to add them**.

- [AWS](https://www.pulumi.com/registry/packages/aws/)
- [Cloudflare](https://www.pulumi.com/registry/packages/cloudflare/)

These are used internally to power the [built-in](/docs/components#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](https://github.com/sst/provider) repo.

---

| Provider                         | Package                                                    |
|----------------------------------|------------------------------------------------------------|
| [ACI](https://www.pulumi.com/registry/packages/aci) | `@netascode/aci` |
| [ACME](https://www.pulumi.com/registry/packages/acme) | `@pulumiverse/acme` |
| [Aiven](https://www.pulumi.com/registry/packages/aiven) | `aiven` |
| [Akamai](https://www.pulumi.com/registry/packages/akamai) | `akamai` |
| [Alibaba Cloud](https://www.pulumi.com/registry/packages/alicloud) | `alicloud` |
| [Amazon EKS](https://www.pulumi.com/registry/packages/eks) | `eks` |
| [Aquasec](https://www.pulumi.com/registry/packages/aquasec) | `@pulumiverse/aquasec` |
| [Artifactory](https://www.pulumi.com/registry/packages/artifactory) | `artifactory` |
| [Astra DB](https://www.pulumi.com/registry/packages/astra) | `@pulumiverse/astra` |
| [Auth0](https://www.pulumi.com/registry/packages/auth0) | `auth0` |
| [Auto Deploy](https://www.pulumi.com/registry/packages/auto-deploy) | `auto-deploy` |
| [AWS API Gateway](https://www.pulumi.com/registry/packages/aws-apigateway) | `aws-apigateway` |
| [AWS](https://www.pulumi.com/registry/packages/aws/) | `aws` |
| [AWS Control Tower](https://www.pulumi.com/registry/packages/awscontroltower) | `@lbrlabs/pulumi-awscontroltower` |
| [AWS IAM](https://www.pulumi.com/registry/packages/aws-iam) | `aws-iam` |
| [AWS Cloud Control](https://www.pulumi.com/registry/packages/aws-native) | `aws-native` |
| [AWS QuickStart Aurora Postgres](https://www.pulumi.com/registry/packages/aws-quickstart-aurora-postgres) | `aws-quickstart-aurora-postgres` |
| [AWS QuickStart Redshift](https://www.pulumi.com/registry/packages/aws-quickstart-redshift) | `aws-quickstart-redshift` |
| [AWS QuickStart VPC](https://www.pulumi.com/registry/packages/aws-quickstart-vpc) | `aws-quickstart-vpc` |
| [AWS S3 Replicated Bucket](https://www.pulumi.com/registry/packages/aws-s3-replicated-bucket) | `aws-s3-replicated-bucket` |
| [AWS Static Website](https://www.pulumi.com/registry/packages/aws-static-website) | `aws-static-website` |
| [AWSx](https://www.pulumi.com/registry/packages/awsx) | `awsx` |
| [AzAPI](https://www.pulumi.com/registry/packages/azapi) | `@ediri/azapi` |
| [Azure Active Directory](https://www.pulumi.com/registry/packages/azuread) | `azuread` |
| [Azure Classic](https://www.pulumi.com/registry/packages/azure) | `azure` |
| [Azure Justrun](https://www.pulumi.com/registry/packages/azure-justrun) | `pulumi-azure-justrun` |
| [Azure Native](https://www.pulumi.com/registry/packages/azure-native) | `azure-native` |
| [Azure Quickstart ACR Geo Replication](https://www.pulumi.com/registry/packages/azure-quickstart-acr-geo-replication) | `azure-quickstart-acr-geo-replication` |
| [Azure QuickStart ACR Geo Replication](https://www.pulumi.com/registry/packages/azure-quickstart-acr-geo-replication/) | `azure-quickstart-acr-geo-replication` |
| [Azure Static Website](https://www.pulumi.com/registry/packages/azure-static-website) | `azure-static-website` |
| [AzureDevOps](https://www.pulumi.com/registry/packages/azuredevops) | `azuredevops` |
| [Buildkite](https://www.pulumi.com/registry/packages/buildkite) | `@pulumiverse/buildkite` |
| [Checkly](https://www.pulumi.com/registry/packages/checkly) | `@checkly/pulumi` |
| [Cisco Catalyst SD-WAN](https://www.pulumi.com/registry/packages/sdwan) | `sdwan` |
| [Cisco ISE](https://www.pulumi.com/registry/packages/ise/) | `ise` |
| [Civo](https://www.pulumi.com/registry/packages/civo) | `civo` |
| [Cloud-Init](https://www.pulumi.com/registry/packages/cloudinit) | `cloudinit` |
| [CloudAMQP](https://www.pulumi.com/registry/packages/cloudamqp) | `cloudamqp` |
| [Cloudflare](https://www.pulumi.com/registry/packages/cloudflare/) | `cloudflare` |
| [CockroachDB](https://www.pulumi.com/registry/packages/cockroach/) | `@pulumiverse/cockroach` |
| [Command](https://www.pulumi.com/registry/packages/command/) | `command` |
| [Confluent](https://www.pulumi.com/registry/packages/confluentcloud/) | `confluentcloud` |
| [Consul](https://www.pulumi.com/registry/packages/consul) | `consul` |
| [Control Plane](https://www.pulumi.com/registry/packages/cpln/) | `@pulumiverse/cpln` |
| [Databricks](https://www.pulumi.com/registry/packages/databricks) | `databricks` |
| [Datadog](https://www.pulumi.com/registry/packages/datadog) | `datadog` |
| [dbt Cloud](https://www.pulumi.com/registry/packages/dbtcloud/) | `dbtcloud` |
| [DigitalOcean](https://www.pulumi.com/registry/packages/digitalocean) | `digitalocean` |
| [DNSimple](https://www.pulumi.com/registry/packages/dnsimple) | `dnsimple` |
| [Docker](https://www.pulumi.com/registry/packages/docker) | `docker` |
| [Docker Build](https://www.pulumi.com/registry/packages/docker-build) | `docker-build` |
| [Doppler](https://www.pulumi.com/registry/packages/doppler) | `@pulumiverse/doppler` |
| [Dynatrace](https://www.pulumi.com/registry/packages/dynatrace) | `@pulumiverse/dynatrace` |
| [Elastic Cloud](https://www.pulumi.com/registry/packages/ec/) | `ec` |
| [Equinix](https://www.pulumi.com/registry/packages/equinix/) | `@equinix-labs/pulumi-equinix` |
| [ESXi Native](https://www.pulumi.com/registry/packages/esxi-native) | `@pulumiverse/esxi-native` |
| [Event Store Cloud](https://www.pulumi.com/registry/packages/eventstorecloud/) | `@eventstore/pulumi-eventstorecloud` |
| [Exoscale](https://www.pulumi.com/registry/packages/exoscale) | `@pulumiverse/exoscale` |
| [F5 BIG-IP](https://www.pulumi.com/registry/packages/f5bigip) | `f5bigip` |
| [Fastly](https://www.pulumi.com/registry/packages/fastly) | `fastly` |
| [Flux](https://www.pulumi.com/registry/packages/flux) | `@worawat/flux` |
| [Fortios](https://www.pulumi.com/registry/packages/fortios) | `@pulumiverse/fortios` |
| [FusionAuth](https://www.pulumi.com/registry/packages/fusionauth) | `pulumi-fusionauth` |
| [Gandi](https://www.pulumi.com/registry/packages/gandi) | `@pulumiverse/gandi` |
| [GCP Global CloudRun](https://www.pulumi.com/registry/packages/gcp-global-cloudrun) | `gcp-global-cloudrun` |
| [Genesis Cloud](https://www.pulumi.com/registry/packages/genesiscloud/) | `@genesiscloud/pulumi-genesiscloud` |
| [GitHub](https://www.pulumi.com/registry/packages/github) | `github` |
| [GitLab](https://www.pulumi.com/registry/packages/gitlab) | `gitlab` |
| [Google Cloud Classic](https://www.pulumi.com/registry/packages/gcp) | `gcp` |
| [Google Cloud Native](https://www.pulumi.com/registry/packages/google-native/) | `google-native` |
| [Google Cloud Static Website](https://www.pulumi.com/registry/packages/google-cloud-static-website/) | `google-cloud-static-website` |
| [Grafana](https://www.pulumi.com/registry/packages/grafana) | `@pulumiverse/grafana` |
| [Harbor](https://www.pulumi.com/registry/packages/harbor) | `@pulumiverse/harbor` |
| [Harness](https://www.pulumi.com/registry/packages/harness) | `harness` |
| [HashiCorp Vault](https://www.pulumi.com/registry/packages/vault) | `vault` |
| [HCP](https://www.pulumi.com/registry/packages/hcp) | `@grapl/pulumi-hcp` |
| [Hetzner Cloud](https://www.pulumi.com/registry/packages/hcloud) | `hcloud` |
| [Impart Security](https://www.pulumi.com/registry/packages/impart/) | `@impart-security/pulumi-impart` |
| [InfluxDB](https://www.pulumi.com/registry/packages/influxdb) | `@komminarlabs/influxdb` |
| [Kafka](https://www.pulumi.com/registry/packages/kafka) | `kafka` |
| [Keycloak](https://www.pulumi.com/registry/packages/keycloak) | `keycloak` |
| [Kong](https://www.pulumi.com/registry/packages/kong) | `kong` |
| [Koyeb](https://www.pulumi.com/registry/packages/koyeb) | `@koyeb/pulumi-koyeb` |
| [Kubernetes](https://www.pulumi.com/registry/packages/kubernetes) | `kubernetes` |
| [Kubernetes Cert Manager](https://www.pulumi.com/registry/packages/kubernetes-cert-manager) | `kubernetes-cert-manager` |
| [Kubernetes CoreDNS](https://www.pulumi.com/registry/packages/kubernetes-coredns) | `kubernetes-coredns` |
| [LaunchDarkly](https://registry.terraform.io/providers/launchdarkly/launchdarkly) | `lauchdarkly` |
| [LBr Labs EKS](https://www.pulumi.com/registry/packages/lbrlabs-eks) | `@lbrlabs/pulumi-eks` |
| [libvirt](https://www.pulumi.com/registry/packages/libvirt) | `libvirt` |
| [Linode](https://www.pulumi.com/registry/packages/linode) | `linode` |
| [Mailgun](https://www.pulumi.com/registry/packages/mailgun) | `mailgun` |
| [Matchbox](https://www.pulumi.com/registry/packages/matchbox) | `@pulumiverse/matchbox` |
| [Miniflux](https://www.pulumi.com/registry/packages/aws-miniflux/) | `aws-miniflux` |
| [MinIO](https://www.pulumi.com/registry/packages/minio) | `minio` |
| [MongoDB Atlas](https://www.pulumi.com/registry/packages/mongodbatlas) | `mongodbatlas` |
| [MSSQL](https://www.pulumi.com/registry/packages/mssql) | `@pulumiverse/mssql` |
| [MySQL](https://www.pulumi.com/registry/packages/mysql) | `mysql` |
| [Neon](https://www.pulumi.com/registry/packages/neon) | `neon` |
| [New Relic](https://www.pulumi.com/registry/packages/newrelic) | `newrelic` |
| [NGINX Ingress Controller](https://www.pulumi.com/registry/packages/kubernetes-ingress-nginx/) | `kubernetes-ingress-nginx` |
| [ngrok](https://www.pulumi.com/registry/packages/ngrok) | `@pierskarsenbarg/ngrok` |
| [Nomad](https://www.pulumi.com/registry/packages/nomad) | `nomad` |
| [NS1](https://www.pulumi.com/registry/packages/ns1) | `ns1` |
| [Nuage](https://www.pulumi.com/registry/packages/nuage) | `nuage` |
| [Nutanix](https://www.pulumi.com/registry/packages/nutanix) | `@pierskarsenbarg/nutanix` |
| [Okta](https://www.pulumi.com/registry/packages/okta) | `okta` |
| [OneLogin](https://www.pulumi.com/registry/packages/onelogin) | `onelogin` |
| [OpenStack](https://www.pulumi.com/registry/packages/openstack) | `openstack` |
| [Opsgenie](https://www.pulumi.com/registry/packages/opsgenie) | `opsgenie` |
| [Oracle Cloud Infrastructure](https://www.pulumi.com/registry/packages/oci) | `oci` |
| [OVHCloud](https://www.pulumi.com/registry/packages/ovh) | `@ovh-devrelteam/pulumi-ovh` |
| [PagerDuty](https://www.pulumi.com/registry/packages/pagerduty) | `pagerduty` |
| [Pinecone](https://www.pulumi.com/registry/packages/pinecone) | `@pinecone-database/pulumi` |
| [PlanetScale](https://github.com/sst/pulumi-planetscale) | `planetscale` |
| [Port](https://www.pulumi.com/registry/packages/port) | `@port-labs/port` |
| [PostgreSQL](https://www.pulumi.com/registry/packages/postgresql) | `postgresql` |
| [Prodvana](https://www.pulumi.com/registry/packages/prodvana) | `@prodvana/pulumi-prodvana` |
| [Proxmox Virtual Environment](https://www.pulumi.com/registry/packages/proxmoxve) | `@muhlba91/pulumi-proxmoxve` |
| [Pulumi Cloud](https://www.pulumi.com/registry/packages/pulumiservice) | `pulumiservice` |
| [purrl](https://www.pulumi.com/registry/packages/purrl) | `@pulumiverse/purrl` |
| [Qovery](https://www.pulumi.com/registry/packages/qovery) | `@ediri/qovery` |
| [RabbitMQ](https://www.pulumi.com/registry/packages/rabbitmq) | `rabbitmq` |
| [Rancher2](https://www.pulumi.com/registry/packages/rancher2) | `rancher2` |
| [Railway](https://registry.terraform.io/providers/terraform-community-providers/railway/latest) | `railway` |
| [random](https://www.pulumi.com/registry/packages/random) | `random` |
| [Redis Cloud](https://www.pulumi.com/registry/packages/rediscloud) | `@rediscloud/pulumi-rediscloud` |
| [Rootly](https://www.pulumi.com/registry/packages/rootly) | `@rootly/pulumi` |
| [Runpod](https://www.pulumi.com/registry/packages/runpod) | `@runpod-infra/pulumi` |
| [Scaleway](https://www.pulumi.com/registry/packages/scaleway) | `@pulumiverse/scaleway` |
| [Sentry](https://www.pulumi.com/registry/packages/sentry) | `@pulumiverse/sentry` |
| [SignalFx](https://www.pulumi.com/registry/packages/signalfx) | `signalfx` |
| [Slack](https://www.pulumi.com/registry/packages/slack) | `slack` |
| [Snowflake](https://www.pulumi.com/registry/packages/snowflake) | `snowflake` |
| [Splight](https://www.pulumi.com/registry/packages/splight) | `@splightplatform/pulumi-splight` |
| [Splunk](https://www.pulumi.com/registry/packages/splunk) | `splunk` |
| [Spotinst](https://www.pulumi.com/registry/packages/spotinst) | `spotinst` |
| [Statuscake](https://www.pulumi.com/registry/packages/statuscake) | `@pulumiverse/statuscake` |
| [Strata Cloud Manager](https://www.pulumi.com/registry/packages/scm) | `scm` |
| [Stripe](https://github.com/georgegebbett/pulumi-stripe) | `stripe` |
| [Stripe Official](https://github.com/stripe/terraform-provider-stripe) | `stripe-official` |
| [StrongDM](https://www.pulumi.com/registry/packages/sdm/) | `@pierskarsenbarg/sdm` |
| [Sumo Logic](https://www.pulumi.com/registry/packages/sumologic) | `sumologic` |
| [Supabase](https://github.com/sst/pulumi-supabase) | `supabase` |
| [Symbiosis](https://www.pulumi.com/registry/packages/symbiosis) | `@symbiosis-cloud/symbiosis-pulumi` |
| [Synced Folder](https://www.pulumi.com/registry/packages/synced-folder) | `synced-folder` |
| [Tailscale](https://www.pulumi.com/registry/packages/tailscale) | `tailscale` |
| [Talos Linux](https://www.pulumi.com/registry/packages/talos) | `@pulumiverse/talos` |
| [Time](https://www.pulumi.com/registry/packages/time) | `@pulumiverse/time` |
| [TLS](https://www.pulumi.com/registry/packages/tls) | `tls` |
| [Twingate](https://www.pulumi.com/registry/packages/twingate) | `@twingate/pulumi-twingate` |
| [Unifi](https://www.pulumi.com/registry/packages/unifi) | `@pulumiverse/unifi` |
| [Upstash](https://www.pulumi.com/registry/packages/upstash) | `@upstash/pulumi` |
| [Venafi](https://www.pulumi.com/registry/packages/venafi) | `venafi` |
| [Vercel](https://www.pulumi.com/registry/packages/vercel) | `vercel` |
| [VMware vSphere](https://www.pulumi.com/registry/packages/vsphere) | `vsphere` |
| [Volcengine](https://www.pulumi.com/registry/packages/volcengine) | `@volcengine/pulumi` |
| [vSphere](https://www.pulumi.com/registry/packages/vsphere) | `vsphere` |
| [Vultr](https://www.pulumi.com/registry/packages/vultr) | `@ediri/vultr` |
| [Wavefront](https://www.pulumi.com/registry/packages/wavefront) | `wavefront` |
| [Yandex](https://www.pulumi.com/registry/packages/yandex) | `yandex` |
| [Zitadel](https://www.pulumi.com/registry/packages/zitadel) | `@pulumiverse/zitadel` |
| [Zscaler Internet Access](https://www.pulumi.com/registry/packages/zia/) | `@bdzscaler/pulumi-zia` |
| [Zscaler Private Access](https://www.pulumi.com/registry/packages/zpa/) | `@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
description: A simple and secure guide to setting 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**](https://portal.aws.amazon.com/billing/signup?type=enterprise#/start/email).

1. Start by using a **work email alias**. For example `aws@acme.com`. 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 `dax@acme.com`, 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 `aws+dev@acme.com` and it'll still go to your `aws@acme.com` 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](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 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.
   
   <details>
   <summary>View script</summary>

   ```sh title="login.sh"
   #!/bin/bash

   if grep -q WSL /proc/version; then
      export BROWSER=wslview
   fi

   aws sso login --sso-session=acme
   ```

   </details>

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
description: The basics of building apps with SST.
---

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.

<Tabs>
  <TabItem label="Type check">
  
  </TabItem>
  <TabItem label="Autocomplete">
  
  </TabItem>
  <TabItem label="Inline help">
  
  </TabItem>
</Tabs>

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](/docs/iam-credentials/).

---

### Console

SST also comes with a [Console](/docs/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](/docs/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](/docs/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](/docs/import-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](/docs/reference-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](/docs/share-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.

<VideoAside title="Watch a video on linking resources" href="https://youtu.be/s8cWklU4Akw" />

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](/docs/reference/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](/docs/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](/docs/state/).

---

#### 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`](reference/cli/#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](/docs/basics/#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](/docs/basics/#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](/docs/basics/#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.

<VideoAside title="Watch a video about dev mode" href="https://youtu.be/mefLc137EB0" />

It deploys your app a little differently and is optimized for local development.

1. It runs the functions in your app [_Live_](/docs/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_](/docs/reference/cli#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`](/docs/reference/cli/#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.

<VideoAside title="Watch a video on how to protect your prod resources" href="https://youtu.be/fb6UBGwgDuA" />

Learn more about [removal policies](/docs/reference/config/#removal).

---

## 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.

<VideoAside title="Watch a video about preview environments and the team workflow" href="https://youtu.be/Pvg_AiTfWeY" />

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](/docs/console/#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
description: Release notes for SST.
---

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

Release notes for SST. For older versions or the full commit history, see the [GitHub releases](https://github.com/sst/sst/releases).

<Changelog />

---

### Www Src Content Docs Docs Cloudflare (`www/src/content/docs/docs/cloudflare.mdx`)

---
title: Cloudflare
description: Learn how to use SST with Cloudflare
---

[Cloudflare](https://cloudflare.com) 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](/docs/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`](/docs/reference/config/#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](https://dash.cloudflare.com/?to=/: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`](/docs/component/cloudflare/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`](/docs/component/cloudflare/bucket/) for R2 and [`Kv`](/docs/component/cloudflare/kv/) for KV namespaces.


### Queue

Use [`Queue`](/docs/component/cloudflare/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`](/docs/component/cloudflare/cron/) to run a worker on a cron expression.

### More components

Browse the component docs for [`Worker`](/docs/component/cloudflare/worker/), [`Astro`](/docs/component/cloudflare/astro/), [`Bucket`](/docs/component/cloudflare/bucket/), [`D1`](/docs/component/cloudflare/d1/), [`Kv`](/docs/component/cloudflare/kv/), [`Queue`](/docs/component/cloudflare/queue/), [`Cron`](/docs/component/cloudflare/cron/), [`Ai`](/docs/component/cloudflare/ai/), [`Workflow`](/docs/component/cloudflare/workflow/), and [`RateLimit`](/docs/component/cloudflare/rate-limit/).

If you are using Cloudflare DNS with SST, use [`sst.cloudflare.dns`](/docs/component/cloudflare/dns/) with [custom domains](/docs/custom-domains/).

---

## Cloudflare Vite plugin

Cloudflare SSR components like [Astro](/docs/component/cloudflare/astro/) or [TanStack Start](/docs/component/cloudflare/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](https://github.com/cloudflare/workers-sdk/pull/13587) 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:

- [Cloudflare Workers with SST](/docs/start/cloudflare/worker/)
- [Hono on Cloudflare with SST](/docs/start/cloudflare/hono/)
- [tRPC on Cloudflare with SST](/docs/start/cloudflare/trpc/)
- [Astro on Cloudflare](https://github.com/sst/sst/tree/dev/examples/cloudflare-astro)
- [Cloudflare D1](https://github.com/sst/sst/tree/dev/examples/cloudflare-d1)
- [Cloudflare KV](https://github.com/sst/sst/tree/dev/examples/cloudflare-kv)
- [Cloudflare Queue](https://github.com/sst/sst/tree/dev/examples/cloudflare-queue)
- [Cloudflare Cron](https://github.com/sst/sst/tree/dev/examples/cloudflare-cron)

---

### Www Src Content Docs Docs Components (`www/src/content/docs/docs/components.mdx`)

---
title: Components
description: Components are the building blocks of your app.
---

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](/docs/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](/docs/all-providers#directory) 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](https://www.pulumi.com/registry/packages/aws/) provider.

---

### Cloudflare

These components are namespaced under **`sst.cloudflare.*`** and listed under Cloudflare in the sidebar. Internally they use Pulumi's [Cloudflare](https://www.pulumi.com/registry/packages/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](/docs/component/aws/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`](/docs/component/linkable/#static-wrap) need to be global across your entire app.

   This allows [Resource Linking](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`](/docs/component/aws/function#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](/docs/component/aws/function#transform) 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`](https://www.pulumi.com/registry/packages/aws/api-docs/iam/role/#constructor-syntax) 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`](/docs/reference/global/#transform).

---

## Properties

An instance of a component exposes a set of properties. For example, the `Function` component exposes the following [properties](/docs/component/aws/function#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.

---

### Links

Some of these properties are also made available via [resource linking](/docs/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](/docs/component/aws/bucket/#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](/docs/component/aws/function#nodes) — `function`, `logGroup`, and `role`.

---

## Outputs

The properties of a component are typically of a special type that looks something like, _`Output<primitive>`_.

<VideoAside title="Watch a video on how inputs and outputs work" href="https://youtu.be/zQTtZS4dIbQ" />

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](https://www.pulumi.com/docs/concepts/inputs-outputs/).

---

### 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`](/docs/reference/global/#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`](/docs/reference/global/#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`](/docs/reference/global/#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`](/docs/reference/global/#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`](/docs/reference/global/#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`](/docs/component/aws/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`](/docs/component/aws/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
description: Create a shared CloudFront distribution for your entire app.
---

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](/docs/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.

<ProdDiagram style="width: min(100%, 400px);" />

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.

<DevDiagram style="width: min(100%, 640px);" />

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
description: Manage and monitor your apps with the SST 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**](https://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.

<a href="https://console.sst.dev" target="_blank" rel="noopener noreferrer">
  <picture>
    <source srcset={consoleHomeDark.src} media="(prefers-color-scheme: dark)" />
    <source srcset={consoleHomeLight.src} media="(prefers-color-scheme: light)" />
    <Image src={consoleHomeLight} alt="SST Console" />
  </picture>
</a>

:::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**](https://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.
   :::

2. **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](https://repost.aws/knowledge-center/lambda-concurrency-limit-increase) to the default value of 1000. You can also do the following to expedite the request.

   <details>
   <summary>Expedite the request</summary>
   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.
   </details>

3. **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](https://github.com/sst/console). 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][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`<br/>`s3:GetObject`<br/>`s3:ListBucket`|
  | Display function logs                  | `logs:DescribeLogStreams`<br/>`logs:FilterLogEvents`<br/>`logs:GetLogEvents`<br/>`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`<br/>`events:PutTargets`                           |
  | Grant event bus access for Console           | `iam:CreateRole`<br/>`iam:DeleteRole`<br/>`iam:DeleteRolePolicy`<br/>`iam:PassRole`<br/>`iam:PutRolePolicy` |
  | Enable Issues to subscribe logs                  | `logs:CreateLogGroup`<br/>`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.

   <details>
   <summary>_View the template changes_</summary>
   
     ```
/* Detailed source-code truncated for AI context efficiency. */
```
   
   </details>

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

<picture>
  <source srcset={consoleLogsDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={consoleLogsLight.src} media="(prefers-color-scheme: light)" />
  <Image src={consoleLogsLight} alt="SST Console Logs" />
</picture>

---

### 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.

<picture>
  <source srcset={consoleIssuesDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={consoleIssuesLight.src} media="(prefers-color-scheme: light)" />
  <Image src={consoleIssuesLight} alt="SST Console Issues" />
</picture>

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](/docs/reference/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](#how-it-works-1).

  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](https://aws.amazon.com/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.

<picture>
  <source srcset={consoleUpdatesDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={consoleUpdatesLight.src} media="(prefers-color-scheme: light)" />
  <Image src={consoleUpdatesLight} alt="SST Console Updates" />
</picture>

The CLI updates your [state](/docs/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](/docs/state/) in your app. You can view:

1. Each resource in your app 
2. The relation between resources
3. The outputs of a given resource

<picture>
  <source srcset={consoleResourcesDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={consoleResourcesLight.src} media="(prefers-color-scheme: light)" />
  <Image src={consoleResourcesLight} alt="SST Console Resources" />
</picture>

---

### Autodeploy

The Console can auto-deploy your apps when you _git push_ to your GitHub repo. Autodeploy uses [AWS CodeBuild](https://aws.amazon.com/codebuild/) in your account to run the build.

<picture>
  <source srcset={consoleAutodeployDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={consoleAutodeployLight.src} media="(prefers-color-scheme: light)" />
  <Image src={consoleAutodeployLight} alt="SST Console Autodeploy" />
</picture>

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`](/docs/reference/config/#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`](/docs/reference/config/#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](https://aws.amazon.com/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.

<picture>
  <source srcset={consoleLocalDark.src} media="(prefers-color-scheme: dark)" />
  <source srcset={consoleLocalLight.src} media="(prefers-color-scheme: light)" />
  <Image src={consoleLocalLight} alt="SST Console Local logs" />
</picture>

:::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`](/docs/reference/cli#cert) command.

```bash
sst cert
```

You'll only need to **run this once** on your machine.

---

#### Gitpod

If you are using [Gitpod](https://www.gitpod.io/), 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](https://www.gitpod.io/blog/local-app#installation)
2. [Run the Companion app](https://www.gitpod.io/blog/local-app#running)
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](#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](https://github.com/sst/console) 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][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][contact-us].


[contact-us]: mailto:hello@sst.dev

---

### Www Src Content Docs Docs Custom Domains (`www/src/content/docs/docs/custom-domains.mdx`)

---
title: Custom Domains
description: Configure custom domains in your components.
---

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.

<Tabs>
  <TabItem label="Frontend">
  ```ts title="sst.config.ts" {2}
  new sst.aws.Nextjs("MyWeb", {
    domain: "example.com"
  });
  ```
  </TabItem>
  <TabItem label="API">
  ```ts title="sst.config.ts" {2}
  new sst.aws.ApiGatewayV2("MyApi", {
    domain: "api.example.com"
  });
  ```
  </TabItem>
  <TabItem label="Service">
  ```ts title="sst.config.ts" {6}
  const vpc = new sst.aws.Vpc("MyVpc");

  new sst.aws.Cluster("MyCluster", {
    vpc,
    loadBalancer: {
      domain: "example.com"
    }
  });
  ```
  </TabItem>
  <TabItem label="Router">
  ```ts title="sst.config.ts" {2}
  new sst.aws.Router("MyRouter", {
    domain: "example.com"
  });
  ```
  </TabItem>
</Tabs>

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](/docs/configure-a-router).
:::

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](https://docs.aws.amazon.com/acm/latest/userguide/domain-ownership-validation.html) 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`](/docs/component/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](#manual-setup).

---

### Vercel

If your domains are hosted on [Vercel](https://vercel.com), you'll need to do the following.

1. [Add the Vercel provider to your app](/docs/component/vercel/dns/#configure-provider).

   ```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`](/docs/component/vercel/dns/) adapter.

   ```js
   {
     domain: {
       name: "example.com",
       dns: sst.vercel.dns()
     }
   }
   ```

---

### Cloudflare

If your domains are hosted on [Cloudflare](https://developers.cloudflare.com/dns/), 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](https://dash.cloudflare.com/?to=/:account/api-tokens) 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](console.mdx#autodeploy) or through your CI, you'll need to set these as environment variables.


3. Use the [`sst.cloudflare.dns`](/docs/component/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](https://docs.aws.amazon.com/acm/latest/userguide/domain-ownership-validation.html) 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
description: Manage the environment variables in your app. 
---

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.

---

## Recommended

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.

---

### Links

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](/docs/reference/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](/docs/linking/).

---

### 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](/docs/component/secret).

---

### 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://foo@sentry.io/bar"
   : "https://baz@sentry.io/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://foo@sentry.io/bar"
   : "https://baz@sentry.io/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`](/docs/reference/global#app) and [`$dev`](/docs/reference/global#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
description: Configure the IAM credentials that's used to deploy your app.
---

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](/docs/aws-accounts).

---

#### 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](https://sst.dev/chapters/create-an-iam-user.html)
2. And then use this to [configure the credentials](https://sst.dev/chapters/configure-the-aws-cli.html)

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_.

<details>
<summary>**Copy IAM Policy**</summary>

```
/* Detailed source-code truncated for AI context efficiency. */
```

</details>

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](/docs/state/#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](/docs/component/secret).

  ```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_](/docs/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](https://aws.amazon.com/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](https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-policy-generation.html).

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
description: Import previously created resources into your app.
---

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](/docs/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](/docs/reference-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`](/docs/components/#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`](/docs/component/aws/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`](https://www.pulumi.com/registry/packages/aws/api-docs/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`](https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketv2/#import).

---

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`](https://www.pulumi.com/registry/packages/aws/api-docs/ec2/vpc/) | VPC ID | `vpc-a01106c2` |
| [`aws.iam.Role`](https://www.pulumi.com/registry/packages/aws/api-docs/iam/role/) | Role name | `role-name` |
| [`aws.sqs.Queue`](https://www.pulumi.com/registry/packages/aws/api-docs/sqs/queue/) | Queue URL | `https://queue.amazonaws.com/80398EXAMPLE/MyQueue` |
| [`aws.sns.Topic`](https://www.pulumi.com/registry/packages/aws/api-docs/sns/topic/) | Topic ARN | `arn:aws:sns:us-west-2:0123456789012:my-topic` |
| [`aws.rds.Cluster`](https://www.pulumi.com/registry/packages/aws/api-docs/rds/cluster/) | Cluster identifier | `aurora-prod-cluster` |
| [`aws.ecs.Service`](https://www.pulumi.com/registry/packages/aws/api-docs/ecs/service/) | Cluster and service name | `cluster-name/service-name` |
| [`aws.ecs.Cluster`](https://www.pulumi.com/registry/packages/aws/api-docs/ecs/cluster/) | Cluster name | `cluster-name` |
| [`aws.s3.BucketV2`](https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketv2/) | Bucket name | `bucket-name` |
| [`aws.kinesis.Stream`](https://www.pulumi.com/registry/packages/aws/api-docs/kinesis/stream/) | Stream name | `my-kinesis-stream` |
| [`aws.dynamodb.Table`](https://www.pulumi.com/registry/packages/aws/api-docs/dynamodb/table/) | Table name | `table-name` |
| [`aws.lambda.Function`](https://www.pulumi.com/registry/packages/aws/api-docs/lambda/function/) | Function name | `function-name` |
| [`aws.apigatewayv2.Api`](https://www.pulumi.com/registry/packages/aws/api-docs/apigatewayv2/api/) | API ID | `12345abcde` |
| [`aws.cognito.UserPool`](https://www.pulumi.com/registry/packages/aws/api-docs/cognito/userpool/) | User Pool ID | `us-east-1_abc123` |
| [`aws.apigateway.RestApi`](https://www.pulumi.com/registry/packages/aws/api-docs/apigateway/restapi/) | REST API ID | `12345abcde` |
| [`aws.cloudwatch.LogGroup`](https://www.pulumi.com/registry/packages/aws/api-docs/cloudwatch/loggroup/) | Log Group name | `my-log-group` |
| [`aws.cognito.IdentityPool`](https://www.pulumi.com/registry/packages/aws/api-docs/cognito/identitypool/) | Identity Pool ID | `us-east-1:1a234567-8901-234b-5cde-f6789g01h2i3` |
| [`aws.cloudfront.Distribution`](https://www.pulumi.com/registry/packages/aws/api-docs/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
description: Link resources together and access them in a typesafe and secure way.
---

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.

<VideoAside title="Watch a video on linking resources" href="https://youtu.be/s8cWklU4Akw" />

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.

   <Tabs>
     <TabItem label="Next.js">
     ```ts title="sst.config.ts" {2}
     new sst.aws.Nextjs("MyWeb", {
       link: [bucket]
     });
     ```
     </TabItem>
     <TabItem label="Remix">
     ```ts title="sst.config.ts" {2}
     new sst.aws.Remix("MyWeb", {
       link: [bucket]
     });
     ```
     </TabItem>
     <TabItem label="Astro">
     ```ts title="sst.config.ts" {2}
     new sst.aws.Astro("MyWeb", {
       link: [bucket]
     });
     ```
     </TabItem>
     <TabItem label="Function">
     ```ts title="sst.config.ts" {3}
     new sst.aws.Function("MyFunction", {
       handler: "src/lambda.handler",
       link: [bucket]
     });
     ```
     </TabItem>
   </Tabs>

3. Use the [SDK](/docs/reference/sdk/) to access the linked resource in your runtime in a typesafe way.

   <Tabs>
     <TabItem label="Next.js">
     ```js title="app/page.tsx"
     import { Resource } from "sst";

     console.log(Resource.MyBucket.name);
     ```
     </TabItem>
     <TabItem label="Remix">
     ```js title="app/routes/_index.tsx"
     import { Resource } from "sst";

     console.log(Resource.MyBucket.name);
     ```
     </TabItem>
     <TabItem label="Astro">
     ```astro title="src/pages/index.astro"
     ---
     import { Resource } from "sst";

     console.log(Resource.MyBucket.name);
     ---
     ```
     </TabItem>
     <TabItem label="Function">
     ```js title="src/lambda.ts"
     import { Resource } from "sst";

     console.log(Resource.MyBucket.name);
     ```
     </TabItem>
   </Tabs>

   :::tip
   The SDK currently supports JS/TS, Python, Golang, and Rust.
   :::

Learn how to use the SDK in [Python](/docs/reference/sdk/#python), [Golang](/docs/reference/sdk/#golang), and [Rust](/docs/reference/sdk/#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`](/docs/reference/cli/#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.

<Tabs>
  <TabItem label="Next.js">
  ```bash
  sst dev next dev
  ```
  </TabItem>
  <TabItem label="Remix">
  ```bash
  sst dev remix dev
  ```
  </TabItem>
  <TabItem label="Astro">
  ```bash
  sst dev astro dev
  ```
  </TabItem>
  <TabItem label="Function">
  ```bash
  sst dev
  ```
  </TabItem>
</Tabs>

---

## 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](/docs/component/aws/bucket/#links).
   :::

2. The types to access these links are generated.

3. The function is given permission to access the linked resource.

---

### Injecting links

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](https://esbuild.github.io/). While bundling, SST injects the resource links into the [`globalThis`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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`](/docs/component/linkable/) component.

---

### Link any value

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](/docs/reference/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`](/docs/component/linkable/).

---

### Link any resource

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](/docs/reference/sdk/) to access it at runtime.

```js title="app/page.tsx"
import { Resource } from "sst";

console.log(Resource.MyTable.tableName);
```

---

### Modify built-in links

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`](/docs/component/linkable/#static-wrap).

---

### Link integration with external providers

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`](/docs/component/linkable/#static-env). [Check out an example](/docs/examples/#aws-linkable-env).

---

### Www Src Content Docs Docs Live (`www/src/content/docs/docs/live.mdx`)

---
title: Live
description: Make changes to your Lambda functions in milliseconds.
---

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](https://docs.aws.amazon.com/appsync/latest/eventapi/event-api-welcome.html) to communicate between your local machine and the remote Lambda function.

When you run `sst dev`, it [bootstraps](/docs/state#bootstrap) 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](https://aws.amazon.com/appsync/pricing/#AppSync_Events_).

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](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/client-authentication.html#mutual) to setup the certificates and import them into your Amazon Certificate Manager.
2. Then [create a Client VPC Endpoint](https://aws.amazon.com/blogs/networking-and-content-delivery/introducing-aws-client-vpn-to-securely-access-aws-and-on-premises-resources/), and associate it with your VPC.
3. And, finally install [Tunnelblick](https://tunnelblick.net) 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](https://aws.amazon.com/vpn/pricing/).

---

## 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
description: Migrate your SST v2 apps to v3.
---

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](/docs/) and [Basics](/docs/basics/) docs.

:::tip
We recently [migrated our demo notes app](https://github.com/sst/demo-notes-app/pull/8/files) 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 <a href={config.discord}>Discord</a>. We've created a channel for folks looking to migrate.

Join `#migrate-from-v2` on <a href={config.discord}>Discord</a>.

---

#### 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](https://github.com/sst/sst/issues/4323) |
| `Function` non-Node.js runtimes | [Container](https://github.com/sst/sst/issues/4462), [Custom](https://github.com/sst/sst/issues/4826) |

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](https://sst.dev/blog/moving-away-from-cdk.html).

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_](/docs/components/#outputs).
3. The state of your app is stored locally and backed up to S3. Learn more about [State](/docs/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](https://www.pulumi.com/registry/packages/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](/docs/all-providers#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`](/docs/components/#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](#sstconfigts-1).

<Tabs>
  <TabItem label="v3">
  ```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() { }
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```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;
  ```
  </TabItem>
</Tabs>

Learn more about the new [`sst.config.ts`](/docs/reference/config/).

---

#### 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`](/docs/reference/cli/#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`](/docs/reference/cli/#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](#clients).

The client handlers and hooks have not been supported yet.

Learn more about [Resource linking](/docs/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**](#sstconfigts-1) 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](https://github.com/sst/demo-notes-app/pull/8) we made these changes.

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  export default $config({
    // ...

    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,
      };
    }
  }
  ```
  </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;
  ```
  </TabItem>
</Tabs>

We store our infrastructure files in the `infra/` directory in v3. You can refer to the [demo notes app](https://github.com/sst/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**](#clients) 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**](#constructs). 

---

#### 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](/docs/import-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.

   <Tabs>
     <TabItem label="v3">
     ```ts title="sst.config.ts"
     new sst.aws.Nextjs("MySite");
     ```
     </TabItem>
     <TabItem label="v2">
     ```ts title="sst.config.ts"
     new NextjsSite(stack, "MySite", {
       customDomain: "domain.com"
     });
     ```
     </TabItem>
   </Tabs>
    
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_**](https://seed.run) to deploy your v2 app; then you'll want to migrate to using [Autodeploy](/docs/console/#autodeploy) on the [SST Console](/docs/console/). We are currently [not planning to support v3 on Seed](https://seed.run/blog/seed-and-sst-v3).

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 — <a href={config.console}>console.sst.dev</a>
2. Link your AWS account
3. Connect your repo
4. Configure your environments
5. And _git push_

Learn more about [Console](/docs/console/) and [Autodeploy](/docs/console/#autodeploy).

---

## sst.config.ts

Listed below are some of the changes to your [`sst.config.ts`](/docs/reference/config/) 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`](/docs/reference/global/#transform).

---

## Clients

The Node.js client, now called the [JS SDK](/docs/reference/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.

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts" {5}
  const bucket = new sst.aws.Bucket("MyBucket");

  new sst.aws.Function("MyFunction", {
    handler: "src/lambda.handler",
    link: [bucket]
  });
  ```
  </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]
  });
  ```
  </TabItem>
</Tabs>

In your function you would access it like so.

<Tabs>
  <TabItem label="v3">
  ```ts title="src/lambda.ts" "Resource.MyBucket.name" "sst"
  import { Resource } from "sst";

  console.log(Resource.MyBucket.name);
  ```
  </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);
  ```
  </TabItem>
</Tabs>

---

### Config

The same applies to `Config` as well. Let's look at a secret.

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts" {5}
  const secret = new sst.Secret("MySecret");

  new sst.aws.Function("MyFunction", {
    handler: "src/lambda.handler",
    link: [secret]
  });
  ```
  </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]
  });
  ```
  </TabItem>
</Tabs>

And in your function you access it in the same way.

<Tabs>
  <TabItem label="v3">
  ```ts title="src/lambda.ts" "Resource.MySecret.value" "sst"
  import { Resource } from "sst";

  console.log(Resource.MySecret.value);
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="src/lambda.ts" "Config.MySecret" "sst/node/config"
  import { Config } from "sst/node/config";

  console.log(Config.MySecret);
  ```
  </TabItem>
</Tabs>

---

### Handlers

In v2, some modules in the Node client had [handlers and hooks](https://v2.sst.dev/clients#handlers).

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

<Tabs>
  <TabItem label="v3">
  ```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");
  ```
  </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"
  });
  ```
  </TabItem>
</Tabs>

---

### Job

The `Task` component that replaces `Job` is based on AWS Fargate. It runs a container task in the background.

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const cluster = new sst.aws.Cluster("MyCluster", { vpc });

  new sst.aws.Task("MyTask", {
    cluster,
    image: {
      context: "./src",
      dockerfile: "Dockerfile"
    }
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new Job(stack, "MyJob", {
    handler: "src/job.main"
  });
  ```
  </TabItem>
</Tabs>

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`](/blog/tasks-in-v3).

---

### RDS

<Tabs>
  <TabItem label="v3">
  ```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"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new RDS(stack, "MyDatabase", {
    engine: "postgresql15.5",
    defaultDatabaseName: "acme",
    migrations: "path/to/migration/scripts"
  });
  ```
  </TabItem>
</Tabs>

The `Aurora` component uses [Amazon Aurora Serverless v2](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html).

For migrations, we recommend using [Drizzle Kit](https://orm.drizzle.team/kit-docs/overview). Check out our [Drizzle example](/docs/start/aws/drizzle/).

---

### Cron

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.Cron("MyCronJob", {
    schedule: "rate(1 minute)",
    function: "src/cron.handler"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new Cron(stack, "MyCronJob", {
    schedule: "rate(1 minute)",
    job: "src/cron.handler"
  });
  ```
  </TabItem>
</Tabs>

---

### Table

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const table = new sst.aws.Dynamo("MyTable", {
    fields: {
      id: "string"
    },
    primaryIndex: { hashKey: "id" }
  });

  table.subscribe("MySubscriber", "src/subscriber.handler");
  ```
  </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"
  });
  ```
  </TabItem>
</Tabs>

---

### Topic

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const topic = new sst.aws.SnsTopic("MyTopic");

  topic.subscribe("MySubscriber", "src/subscriber.handler");
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  const topic = new Topic(stack, "MyTopic");

  topic.addSubscribers(stack, {
    subscriber: "src/subscriber.handler"
  });
  ```
  </TabItem>
</Tabs>

---

### Queue

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const queue = new sst.aws.Queue("MyQueue");

  queue.subscribe("src/subscriber.handler");
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  const queue = new Queue(stack, "MyQueue");

  queue.addConsumer(stack, "src/subscriber.handler");
  ```
  </TabItem>
</Tabs>

---

### Config

The `Config` construct is now broken into a `Secret` component and v3 has a separate way to bind any value or _parameter_.

---

#### Secret

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const secret = new sst.Secret("MySecret");
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  const secret = new Config.Secret(stack, "MySecret");
  ```
  </TabItem>
</Tabs>

There's also a slight change to the CLI for setting secrets.

<Tabs>
  <TabItem label="v3">
  ```bash "secret"
  npx sst secret set MySecret sk_test_abc123
  ```
  </TabItem>
  <TabItem label="v2">
  ```bash "secrets"
  npx sst secrets set MySecret sk_test_abc123
  ```
  </TabItem>
</Tabs>


---

#### Parameter

The `Linkable` component lets you bind or _link_ any value.

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const secret = new sst.Linkable("MyParameter", {
    properties: { version: "1.2.0" }
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  const secret = new Config.Parameter(stack, "MyParameter", {
    value: "1.2.0"
  });
  ```
  </TabItem>
</Tabs>

In your function you'd access this using.

<Tabs>
  <TabItem label="v3">
  ```ts title="src/lambda.ts"
  import { Resource } from "sst";

  console.log(Resource.MyParameter.version);
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="src/lambda.ts"
  import { Config } from "sst/node/config";

  console.log(Config.MyParameter);
  ```
  </TabItem>
</Tabs>

---

### Bucket

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const bucket = new sst.aws.Bucket("MyBucket");

  bucket.subscribe("src/subscriber.handler");
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  const bucket = new Bucket(stack, "MyBucket");

  bucket.addNotifications(stack, {
    notification: "src/notification.main"
  });
  ```
  </TabItem>
</Tabs>

---

### Service

<Tabs>
  <TabItem label="v3">
  ```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" }
      ]
    }
  });
  ```
  </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"
      })
    }
  });
  ```
  </TabItem>
</Tabs>

---

### Cognito

<Tabs>
  <TabItem label="v3">
  ```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
    }]
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new Cognito(stack, "MyAuth");
  ```
  </TabItem>
</Tabs>

---

### Function

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.Function("MyFunction", {
    handler: "src/lambda.handler"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new Function(stack, "MyFunction", {
    handler: "src/lambda.handler"
  });
  ```
  </TabItem>
</Tabs>

---

### AstroSite
<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.Astro("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new AstroSite(stack, "MyWeb", {
    customDomain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

---

### EventBus

<Tabs>
  <TabItem label="v3">
  ```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"]
    }
  });
  ```
  </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"
        }
      }
    }
  });
  ```
  </TabItem>
</Tabs>

---

### StaticSite

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.StaticSite("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new StaticSite(stack, "MyWeb", {
    customDomain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

---

### RemixSite

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.Remix("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new RemixSite(stack, "MyWeb", {
    customDomain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

---

### NextjsSite

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.Nextjs("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new NextjsSite(stack, "MyWeb", {
    customDomain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

---

### AppSyncApi

<Tabs>
  <TabItem label="v3">
  ```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
  });
  ```
  </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"
  });
  ```
  </TabItem>
</Tabs>

---

### SvelteKitSite

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.SvelteKit("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new SvelteKitSite(stack, "MyWeb", {
    customDomain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

---

### SolidStartSite

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  new sst.aws.SolidStart("MyWeb", {
    domain: "my-app.com"
  });
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  new SolidStartSite(stack, "MyWeb", {
    customDomain: "my-app.com"
  });
  ```
  </TabItem>
</Tabs>

---

### WebSocketApi

<Tabs>
  <TabItem label="v3">
  ```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");
  ```
  </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"
  });
  ```
  </TabItem>
</Tabs>

---

### KinesisStream

<Tabs>
  <TabItem label="v3">
  ```ts title="sst.config.ts"
  const stream = new sst.aws.KinesisStream("MyStream");

  stream.subscribe("MySubscriber", "src/subscriber.handler");
  ```
  </TabItem>
  <TabItem label="v2">
  ```ts title="sst.config.ts"
  const stream = new KinesisStream(stack, "MyStream");

  stream.addConsumers(stack, {
    consumer: "src/subscriber.handler"
  });
  ```
  </TabItem>
</Tabs>

---

### ApiGatewayV1Api

<Tabs>
  <TabItem label="v3">
  ```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();
  ```
  </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"
  });
  ```
  </TabItem>
</Tabs>

---

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
description: Migrate your SST v3 apps to v4.
---

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

SST v4 upgrades the underlying [Pulumi AWS provider](https://www.pulumi.com/registry/packages/aws/) 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](https://www.pulumi.com/registry/packages/aws/how-to-guides/7-0-migration).

---

## 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](https://www.pulumi.com/registry/packages/aws/how-to-guides/7-0-migration) 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](https://github.com/anomalyco/sst/pull/6259/).

---

## 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](/docs/share-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
description: Learn how to use SST with PlanetScale
---

[PlanetScale](https://planetscale.com) 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](/docs/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](https://app.planetscale.com/~/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](/docs/console/#autodeploy) in the SST Console to do this automatically on every pull request.
:::

This is a pattern used in production by [terminal.shop](https://github.com/terminaldotshop/terminal) and [OpenCode](https://github.com/anomalyco/opencode).

---

## Link to your app

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`](/docs/component/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](/docs/linking).

---

## 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](https://orm.drizzle.team).

```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:

- [PlanetScale with Drizzle and MySQL](/docs/examples/#aws-planetscale-drizzle-mysql)
- [PlanetScale with Drizzle and Postgres](/docs/examples/#aws-planetscale-drizzle-postgres)

---

### Www Src Content Docs Docs Policy Packs (`www/src/content/docs/docs/policy-packs.mdx`)

---
title: Policy Packs
description: Enforce compliance and security rules on your infrastructure before deploying.
---

Policy packs let you enforce rules on your infrastructure before deploying. They use [Pulumi Policy Packs](https://www.pulumi.com/docs/iac/packages-and-automation/crossguard/) 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](https://github.com/sst/sst/tree/dev/examples/aws-policy-pack) and the [Pulumi Policy Pack docs](https://www.pulumi.com/docs/iac/packages-and-automation/crossguard/) for more details.

---

### Www Src Content Docs Docs Providers (`www/src/content/docs/docs/providers.mdx`)

---
title: Providers
description: Providers allow you to interact with cloud services. 
---

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`.

<VideoAside title="Watch a video about providers in SST" href="https://youtu.be/rlR2f5N9mW4" />

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](/docs/all-providers#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](/docs/all-providers#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`](/docs/reference/cli/#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](https://www.pulumi.com/registry/packages/aws/api-docs/provider/#inputs) and [Cloudflare](https://www.pulumi.com/registry/packages/cloudflare/api-docs/provider/#inputs).

---

### 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](/docs/cloudflare/) 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](/docs/reference/config/#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](/docs/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<primitive>`_. Read more about [Outputs](/docs/components/#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
description: Reference externally created resources in your app.
---

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`](https://www.pulumi.com/registry/packages/aws/api-docs/s3/bucketv2/#look-up) 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](/docs/share-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](/docs/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](/docs/import-resources/#import-properties).
:::

We've compiled a list of the most commonly lookedup low-level resources and their [Import Properties](/docs/import-resources/#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`](/docs/component/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.

---

#### Link to it

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](/docs/reference/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
description: A TypeScript monorepo setup for your app.
---

While, [drop-in mode](/docs/#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](https://github.com/sst/monorepo-template/tree/main) 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**](https://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](https://vitest.dev/) 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`](https://www.npmjs.com/package/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
description: Share resources across stages in your app.
---

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:

- [`Vpc`](/docs/component/aws/vpc/)
- [`Email`](/docs/component/aws/email/)
- [`Bucket`](/docs/component/aws/bucket/)
- [`Postgres`](/docs/component/aws/postgres/)
- [`CognitoUserPool`](/docs/component/aws/cognito-user-pool/)
- [`CognitoIdentityPool`](/docs/component/aws/cognito-identity-pool/)
- [`D1`](/docs/component/cloudflare/d1/)

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`](/docs/component/aws/bucket/#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`](/docs/reference/global/#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`](/docs/reference/config/#run) function.

---

### Www Src Content Docs Docs State (`www/src/content/docs/docs/state.mdx`)

---
title: State
description: Tracking the infrastructure created by your app.
---

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](/docs/reference/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](/docs/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.

<VideoAside title="Watch a video about how state works" href="https://youtu.be/eb1ydrqEFUw" />

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
description: How to upgrade your database and minimize downtime.
---

Sometimes database components like [`Postgres`](/docs/component/aws/postgres/) and [`Mysql`](/docs/component/aws/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`](/docs/component/aws/postgres/) and [`Mysql`](/docs/component/aws/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](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html) 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`](/docs/component/aws/postgres/) and [`Mysql`](/docs/component/aws/mysql/) support [AWS RDS Blue/Green deployments](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/blue-green-deployments.html) 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](https://www.pulumi.com/blog/aws-rds-blue-green-deployment-updates).

---

## 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.

- [PostgreSQL upgrade paths](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.PostgreSQL.html)
- [MySQL upgrade paths](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_UpgradeDBInstance.MySQL.html)
- [Aurora PostgreSQL upgrade paths](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/USER_UpgradeDBInstance.PostgreSQL.html)
- [Aurora MySQL upgrade paths](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Updates.MajorVersionUpgrade.html)

### 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`](/docs/component/aws/postgres/) | ✓ | ✓ | ✓ |
| [`Mysql`](/docs/component/aws/mysql/) | ✓ | ✓ | ✓ |
| [`Aurora`](/docs/component/aws/aurora/) | ✓ | ✕ | ✕ |
| [`Redis`](/docs/component/aws/redis/) | ✓ | ✕ | ✕ |
| [`OpenSearch`](/docs/component/aws/open-search/) | ✓ | ✕ | ✕ |

### 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
description: A page to test all the markdown content test cases
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.

### Links

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.

<p style="color: var(--color-text-secondary)">Line secondary text color.</p>

<p style="color: var(--color-text-dimmed)">Line dimmed text color.</p>

Normal <span style="color: var(--color-text-secondary)">secondary</span> <span style="color: var(--color-text-dimmed)">dimmed</span>

## 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. 

<Tabs>
  <TabItem label="Stars">Sirius, Vega, Betelgeuse</TabItem>
  <TabItem label="Moons">Io, Europa, Ganymede</TabItem>
</Tabs>

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

<Tabs>
<TabItem label="npm">

```bash
npm create astro@latest -- --template starlight
```

</TabItem>
<TabItem label="pnpm">

```bash
pnpm create astro --template starlight
```

</TabItem>
<TabItem label="Yarn">

```bash
yarn create astro --template starlight
```

</TabItem>
</Tabs>

#### Without Frame

<Tabs>
<TabItem label="npm">

```bash frame="none"
npm create astro@latest -- --template starlight
```

</TabItem>
<TabItem label="pnpm">

```bash frame="none"
pnpm create astro --template starlight
```

</TabItem>
<TabItem label="Yarn">

```bash frame="none"
yarn create astro --template starlight
```

</TabItem>
</Tabs>

### 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
1. Item
1. Item
1. Item
1. Item
1. Item
1. Item
1. Item
1. Item
1. Item
1. Item
1. 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.
:::

### With Links

:::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”](https://docs.astro.build/en/concepts/islands/).
:::

### Video Asides

<VideoAside title="Watch a video about SST" href="https://www.youtube.com/watch?v=aPmyoZe3xDE" />

## Extra

## Sections

## At The

## Bottom

## For The

## Right Sidebar

## 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
description: A page to test all the TSDoc cases
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';

<div class="tsdoc">

<Section type="about">
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](/).
</Section>

---

## Constructor

<Segment>
<Section type="signature">
```ts
new FormatSpecifier(specifier, addend?): FormatSpecifier
```
</Section>

<Section type="parameters">
#### Parameters

- <p><code class="key">specifier</code> [<code class="type">FormatSpecifierObject</code>](#FormatSpecifierObject)</p>

  A specifier object.


- <p><code class="key">addend?</code> <code class="primitive">number</code><code class="symbol"> | </code><code class="primitive">string</code><code class="symbol"> & </code><code class="primitive">boolean</code></p>

  A number to add.
</Section>

<InlineSection>
**Returns** [<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol">[]</code><code class="symbol"> | </code><code class="primitive">null</code>
</InlineSection>
</Segment>

## Methods

### noop

<Segment>
<Section type="signature">
```ts
noop(): void
```
</Section>

This function doesn't do anything.
</Segment>

### simpleArgs

<Segment>
<Section type="signature">
```ts
simpleArgs(true, false ,true): void
```
</Section>

This function takes some simple args.

<Section type="parameters">
#### Parameters

- <p><code class="key">isTrue</code> <code class="primitive">boolean</code></p>
- <p><code class="key">isFalse?</code> <code class="primitive">boolean</code></p>
- <p><code class="key">isAny?</code> <code class="primitive">boolean</code></p>
</Section>

</Segment>

### fullArgs

<Segment>
<Section type="signature">
```ts
fullArgs(true, false ,true): FormatSpecifier
```
</Section>

This function takes full args.

<Section type="parameters">
<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>

- <p><code class="key">prop1?</code> <code class="primitive">boolean</code></p>

  <InlineSection>
  **Default** <code class="primitive">true</code>
  </InlineSection>

  A simple prop to add.

- <p><code class="key">prop2?</code> <code class="symbol">&ldquo;</code><code class="primitive">x86_64</code><code class="symbol">&rdquo;</code><code class="symbol"> | </code><code class="symbol">&ldquo;</code><code class="primitive">arm_64</code><code class="symbol">&rdquo;</code></p>

  <InlineSection>
  **Default** Uses x86 by default `x86_64`. But this description of the default is really long and runs more than one single line.
  </InlineSection>

  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"
  }
  ```

- <p><code class="key">prop3?</code> [<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol">[]</code><code class="symbol"> | </code><code class="primitive">null</code></p>

  <InlineSection>
  **Default** <code class="primitive">true</code>
  </InlineSection>

  Just pass something in.

  ```ts
  {
    prop2: "x86_64"
  }
  ```
</Section>

<InlineSection>
**Returns** [<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol">[]</code><code class="symbol"> | </code><code class="primitive">null</code>
</InlineSection>

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);
```

</Segment>

## Properties

### primitiveProp

<Segment>
The ARN of the resource.

<InlineSection>
**Type** <code class="symbol">&ldquo;</code><code class="primitive">x86_64</code><code class="symbol">&rdquo;</code><code class="symbol"> | </code><code class="symbol">&ldquo;</code><code class="primitive">arm_64</code><code class="symbol">&rdquo;</code>
</InlineSection>
</Segment>

### objectProp

<Segment>
A simple object prop.

<Section type="parameters">
<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>

- <p><code class="key">resource1</code> <code class="primitive">string</code></p>
- <p><code class="key">resource2</code> [<code class="type">FormatSpecifier</code>](#FormatSpecifier)</p>
</Section>
</Segment>

### objectPropNested

<Segment>
A nested object prop.

<Section type="parameters">
<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>
- <p>[<code class="key">resource1</code>](#) <code class="primitive">Output</code><code class="symbol">&lt;</code><code class="primitive">Object</code><code class="symbol">&gt;</code></p>
  - <p>[<code class="key">objectKey1</code>](#) <code class="primitive">Object</code></p>
    - <p>[<code class="key">objectKey11</code>](#) <code class="primitive">Object</code><code class="symbol">[]</code></p>
      - <p>[<code class="key">key111</code>](#) <code class="primitive">boolean</code></p>
      - <p>[<code class="key">objectKey112</code>](#) <code class="primitive">Object</code></p>
        - <p>[<code class="key">key1111</code>](#) <code class="primitive">string</code></p>
        - <p>[<code class="key">key1112</code>](#) <code class="primitive">string</code></p>
  - <p>[<code class="key">key2</code>](#) <code class="primitive">boolean</code></p>
- <p>[<code class="key">resource2</code>](#) [<code class="type">FormatSpecifier</code>](#FormatSpecifier)</p>
</Section>

</Segment>

<NestedTitle id="1" Tag="h4" parent="objectPropNested.">objectKey1</NestedTitle>

<Segment>
A simple object prop.

<InlineSection>
**Type** <code class="primitive">Output</code><code class="symbol">&lt;</code><code class="primitive">Object</code><code class="symbol">&gt;</code>
</InlineSection>
</Segment>

<NestedTitle id="2" Tag="h5" parent="objectPropNested.resource1.">objectKey1</NestedTitle>

<Segment>
A simple object prop.

<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>
</Segment>

<NestedTitle id="3" Tag="h5" parent="objectPropNested.resource1.objectKey1.">objectKey11</NestedTitle>

<Segment>
A simple object prop.

<InlineSection>
**Type** <code class="primitive">Object</code><code class="symbol">[]</code>
</InlineSection>
</Segment>

<NestedTitle id="4" Tag="h5" parent="objectPropNested.resource1.objectKey1.objectKey11[].">objectKey112</NestedTitle>

<Segment>
A simple object prop.

<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>
</Segment>

<NestedTitle id="5" Tag="h5" parent="objectPropNested.resource1.objectKey1.objectKey11[].objectKey112.">key1111</NestedTitle>

<Segment>
A simple prop.

<InlineSection>
**Type** <code class="primitive">string</code>
</InlineSection>
</Segment>

## DummyArgs

### simpleArg

<Segment>
The CPU architecture of the lambda function.

<InlineSection>
**Type** <code class="symbol">&ldquo;</code><code class="primitive">x86_64</code><code class="symbol">&rdquo;</code><code class="symbol"> | </code><code class="symbol">&ldquo;</code><code class="primitive">arm_64</code><code class="symbol">&rdquo;</code>
</InlineSection>

<InlineSection>
**Default** Uses x86 by default `x86_64`. But this description of the default is really long and runs more than one single line.
</InlineSection>

For example,

```ts
new Function(stack, "Function", {
  architecture: "arm_64",
})
```
</Segment>

### objectPropSimple?

<Segment>
A prop that takes an object with simple keys.

<Section type="parameters">
<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>

- <p><code class="key">isTrue</code> <code class="primitive">boolean</code></p>
- <p><code class="key">isFalse?</code> <code class="primitive">boolean</code></p>
- <p><code class="key">isAny?</code> <code class="primitive">boolean</code></p>
</Section>

<InlineSection>
**Default** <code class="primitive">undefined</code>
</InlineSection>

```ts
new Function(stack, "Function", {
  architecture: "arm_64",
})
```
</Segment>

### objectPropFull?

<Segment>
A prop that takes an object with full keys.

<Section type="parameters">
<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>

- <p><code class="key">prop1?</code> <code class="primitive">boolean</code></p>

  <InlineSection>
  **Default** <code class="primitive">true</code>
  </InlineSection>

  A simple prop to add.

  ```ts
  {
    prop1: true
  }
  ```

- <p><code class="key">prop2?</code> <code class="symbol">&ldquo;</code><code class="primitive">x86_64</code><code class="symbol">&rdquo;</code><code class="symbol"> | </code><code class="symbol">&ldquo;</code><code class="primitive">arm_64</code><code class="symbol">&rdquo;</code></p>

  <InlineSection>
  **Default** Uses x86 by default `x86_64`. But this description of the default is really long and runs more than one single line.
  </InlineSection>

  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"
  }
  ```

- <p><code class="key">prop3?</code> [<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol">[]</code><code class="symbol"> | </code><code class="primitive">null</code></p>

  <InlineSection>
  **Default** <code class="primitive">true</code>
  </InlineSection>

  Just pass something in.

  ```ts
  {
    prop2: "x86_64"
  }
  ```
</Section>

<InlineSection>
**Default** <code class="primitive">undefined</code>
</InlineSection>

```ts
new Function(stack, "Function", {
  architecture: "arm_64",
})
```
</Segment>

### transform?

<Segment>
[Transform](/docs/transform/) how this component is created.

<Section type="parameters">
<InlineSection>
**Type** <code class="primitive">Object</code>
</InlineSection>

- <p><code class="key">resource1?</code> [<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol"> | </code><code class="symbol">(</code><code class="primitive">args</code><code class="symbol">: </code>[<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol"> => </code>[<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol"> | </code><code class="primitive">void</code><code class="symbol">)</code></p>
- <p><code class="key">resource1?</code> [<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol"> | </code><code class="symbol">(</code><code class="primitive">args</code><code class="symbol">: </code>[<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol"> => </code>[<code class="type">FormatSpecifier</code>](#FormatSpecifier)<code class="symbol"> | </code><code class="primitive">void</code><code class="symbol">)</code></p>
</Section>

<InlineSection>
**Default** <code class="primitive">undefined</code>
</InlineSection>
</Segment>

</div>

---

### Www Src Content Docs Legal Privacy Policy (`www/src/content/docs/legal/privacy-policy.mdx`)

---
template: splash
title: Privacy Policy
description: Privacy policy for sst.dev and the SST Console.
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}</a> (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}</a>

    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}</a> and write “Privacy Policy” in the subject line.

Still Need Help? Email <a href={`mailto:${config.email}`}>{config.email}</a>.

---

### Www Src Content Docs Legal Terms Of Service (`www/src/content/docs/legal/terms-of-service.mdx`)

---
template: splash
title: Terms of Service
description: Terms of service for the SST Console.
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](/legal/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 <u>Compliance with Laws.</u> 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 <u>Unauthorized Use; False Information.</u> 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 <u>Insurance.</u> 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 <u>Customer Responsibility.</u> 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 <u>Ownership.</u> The Customer retains ownership of its Customer Data, including all intellectual property rights therein and relating thereto.

   6.3 <u>License from Customer.</u> 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 <u>Aggregated Data.</u> 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 <u>Transfer Abroad.</u> AI may transfer Customer Data outside of Customer’s jurisdiction of residence for processing and storage.

   6.6 <u>Security.</u> 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 <u>Termination.</u> 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 <u>Free Trial.</u> 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 <u>Subscription Plans.</u> 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](https://sst.dev/docs/console#pricing) (the “Fee Schedule”). AI may also offer certain enterprise Customers custom-made Subscription Plans.

   7.3 <u>Subscription Fees.</u> The monthly subscription fee (the “Subscription Fee”) for each Subscription Plan is set out in the Fee Schedule.

   7.4 <u>Billing and Payment.</u> 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 <u>Taxes.</u> 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 <u>Renewal.</u> 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 <u>Cancellation.</u> 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 <u>No Refunds.</u> All Fee payments are non-refundable and there are no refunds or credits for partially used periods.

   7.9 <u>Price and Plan Changes.</u> 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 <u>Term of Agreement.</u> 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 <u>Termination.</u> 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 <u>Suspension of Service.</u> 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 <u>Effect of Termination.</u> 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 <u>No Refund of Fees.</u> 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 <u>Warranty.</u> 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 <u>Warranty Disclaimer.</u> 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 <u>Limitation of Liability.</u> 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 <u>Exclusion of Consequential and Related Damages.</u> 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 <u>Connectivity.</u> 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 <u>Indemnification by AI.</u> 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 <u>Indemnification by Customer.</u> 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 <u>Conditions for Indemnification.</u> 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 <u>Non-Exclusive Service.</u> 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 <u>Assignment.</u> 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 <u>Subcontractors.</u> 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 <u>Notices.</u> Except as otherwise permitted in this Agreement, notices under this Agreement shall be made in writing to the other party.

    12.5 <u>Force Majeure.</u> 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 <u>Waiver.</u> 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 <u>Severability.</u> 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 <u>Entire Agreement.</u> 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 <u>Survival.</u> 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 <u>Publicity.</u> AI may include the Customer’s name and logo in its customer lists and on its website.

    12.11 <u>Independent Contractor.</u> 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 <u>Governing Law.</u> This Agreement shall be governed by the laws of the State of Delaware and the federal laws applicable therein.

    12.13 <u>Compliance with Laws.</u> 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 <u>Dispute Resolution.</u> 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
description: Interact with your infrastructure in your runtime code.
---

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](https://www.npmjs.com/package/sst) that you can install in your functions, frontends, or container applications.

```bash
npm install sst
```

---

### Links

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`](/docs/component/aws/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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) using [esbuild](https://esbuild.github.io/) 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](/docs/linking/#injecting-links).

---

## Python

SST uses [uv](https://docs.astral.sh/uv/) to package your Python functions. Your functions need to be in a [uv workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/#workspace-sources).

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
description: Create and deploy an Analog app to AWS with SST.
---

We are going to create an [Analog app](https://analogjs.org/), add an S3 Bucket for file uploads, and deploy it to AWS using SST.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-analog) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#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.

#### Link the bucket

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](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) 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
description: Create and deploy an Angular app to 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](https://github.com/sst/sst/tree/dev/examples/aws-angular) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#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`](https://www.npmjs.com/package/@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](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) 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
description: Create and deploy an Astro site to AWS with SST.
---

There are two ways to deploy an Astro site to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#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.

- [Enabling streaming in your Astro app](/docs/examples/#aws-astro-streaming)
- [Hit counter with Redis and Astro in a container](/docs/examples/#aws-astro-container-with-redis)

---

## 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](https://github.com/sst/sst/tree/dev/examples/aws-astro) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#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.

##### Link the bucket

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](https://github.com/sst/sst/tree/dev/examples/aws-astro-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#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](https://docs.astro.build/en/guides/integrations-guide/node/) 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](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/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.

---

##### Link the bucket

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](https://www.docker.com/products/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](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) 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
description: Add OpenAuth to your Next.js app and deploy it with SST.
---

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](https://openauth.js.org), and deploy it with [OpenNext](https://opennext.js.org) and SST.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-auth-nextjs) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

#### Examples

We also have another OpenAuth example that you can refer to.

- [Full-stack React SPA with an API](/docs/examples/#aws-openauth-react-spa)

---

## 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`.

```
/* 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.

<Image src={nextApp} alt="Next.js app login with OpenAuth" />

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](/docs/console/) to _**git push to deploy**_ your app and view logs from it.

<Image src={consoleAutodeploy} alt="SST Console Autodeploy" />

You can [create a free account](https://console.sst.dev) 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
description: Create and deploy a Bun app to 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](https://github.com/sst/sst/tree/dev/examples/aws-bun) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

#### Examples

We also have a few other Bun examples that you can refer to.

- [Deploy Bun with Elysia in a container](/docs/examples/#aws-bun-elysia-container)
- [Build a hit counter with Bun and Redis](/docs/examples/#aws-bun-redis)

---

## 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"
const server = Bun.serve({
  async fetch(req) {
    const url = new URL(req.url);

    if (url.pathname === "/" && req.method === "GET") {
      return new Response("Hello World!");
    }

    return new Response("404!");
  },
});

console.log(`Listening on ${server.url}`);
```

This starts up an HTTP server by default on port `3000`.

---

#### Add scripts

Add the following to your `package.json`.

```json title="package.json"
"scripts": {
  "dev": "bun run --watch index.ts"
},
```

This adds a `dev` script with a watcher.

---

#### Init SST

Now let's initialize SST in our app.

```bash
bunx sst init
bun install
```

This'll create an `sst.config.ts` file in your project root and install SST.

---

## 2. Add a Service

To deploy our Bun app, let's add an [AWS Fargate](https://aws.amazon.com/fargate/) container with [Amazon ECS](https://aws.amazon.com/ecs/). Update your `sst.config.ts`.

```js 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: "3000/http" }],
    },
    dev: {
      command: "bun dev",
    },
  });
}
```

This creates a VPC with an ECS Cluster, and adds a Fargate service to it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Bun app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Bun app.

```bash
bun sst dev
```

Once complete, click on **MyService** in the sidebar and open your Bun app in your browser.

---

## 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Add this to your `sst.config.ts` below the `Vpc` component.

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

---

#### Link the bucket

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 Bun app.

---

## 4. Upload a file

We want a `POST` request made to the `/` route to upload a file to our S3 bucket. Let's add this below our _Hello World_ route in our `index.ts`.

```ts title="index.ts" {5}
if (url.pathname === "/" && req.method === "POST") {
  const formData = await req.formData();
  const file = formData.get("file")! as File;
  const params = {
    Bucket: Resource.MyBucket.name,
    ContentType: file.type,
    Key: file.name,
    Body: file,
  };
  const upload = new Upload({
    params,
    client: s3,
  });
  await upload.done();

  return new Response("File uploaded successfully.");
}
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the imports. We'll use the extra ones below.

```ts title="index.ts"
import { Resource } from "sst";
import {
  S3Client,
  GetObjectCommand,
  ListObjectsV2Command,
} from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3 = new S3Client();
```

And install the npm packages.

```bash
bun install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
```

---

## 5. Download the file

We'll add a `/latest` route that'll download the latest file in our S3 bucket. Let's add this below our upload route in `index.ts`.

```ts title="index.ts"
if (url.pathname === "/latest" && req.method === "GET") {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );
  const latestFile = objects.Contents!.sort(
    (a, b) =>
      (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0),
  )[0];
  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });
  return Response.redirect(await getSignedUrl(s3, command));
}
```

---

#### Test your app

To upload a file run the following from your project root.

```bash
curl -F file=@package.json http://localhost:3000/
```

This should upload the `package.json`. Now head over to `http://localhost:3000/latest` in your browser and it'll show you what you just uploaded.



---

## 6. Deploy your app

To deploy our app we'll first add a `Dockerfile`.

```dockerfile title="Dockerfile"
FROM oven/bun

COPY bun.lock . 
COPY package.json . 

RUN bun install --frozen-lockfile

COPY . .

EXPOSE 3000
CMD ["bun", "index.ts"]
```

This is a pretty basic setup. You can refer to the [Bun docs](https://bun.sh/guides/ecosystem/docker) for a more optimized Dockerfile.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
.git
.gitignore
README.md
Dockerfile*
```

Now to build our Docker image and deploy we run:

```bash
bun sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production. This'll give the URL of your Bun app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://prod-MyServiceLoadBalanc-491430065.us-east-1.elb.amazonaws.com
```

Congrats! Your app should now be live!

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Deno (`www/src/content/docs/docs/start/aws/deno.mdx`)

---
title: Deno on AWS with SST
description: Create and deploy a Deno app to AWS with SST.
---

We are going to build an app with Deno, 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](https://github.com/sst/sst/tree/dev/examples/aws-deno) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

#### Examples

We also have a few other Deno examples that you can refer to.

- [Build a hit counter with Deno and Redis](/docs/examples/#aws-deno-redis)

---

## 1. Create a project

Let's start by creating our Deno app.

```bash
deno init aws-deno
```

---

#### Init Deno Serve

Replace your `main.ts` with the following.

```ts title="main.ts"
Deno.serve(async (req) => {
  const url = new URL(req.url);

  if (url.pathname === "/" && req.method === "GET") {
    return new Response("Hello World!");
  }

  return new Response("404!");
});
```

This starts up an HTTP server by default on port `8000`.

---

#### Init SST

Make sure you have [SST installed globally](/docs/reference/cli).

```bash
sst init
```

This'll create an `sst.config.ts` file in your project root.

---

## 2. Add a Service

To deploy our Deno app, let's add an [AWS Fargate](https://aws.amazon.com/fargate/) container with [Amazon ECS](https://aws.amazon.com/ecs/). Update your `sst.config.ts`.

```js 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: "8000/http" }],
    },
    dev: {
      command: "deno task dev",
    },
  });
}
```

This creates a VPC with an ECS Cluster, and adds a Fargate service to it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Deno app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Deno app.

```bash
sst dev
```

Once complete, click on **MyService** in the sidebar and open your Deno app in your browser.

---

## 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Add this to your `sst.config.ts` below the `Vpc` component.

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

---

#### Link the bucket

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 Deno app.

---

## 4. Upload a file

We want a `POST` request made to the `/` route to upload a file to our S3 bucket. Let's add this below our _Hello World_ route in our `main.ts`.

```ts title="main.ts" {6}
if (url.pathname === "/" && req.method === "POST") {
  const formData: FormData = await req.formData();
  const file: File | null = formData?.get("file") as File;

  const params = {
    Bucket: Resource.MyBucket.name,
    ContentType: file.type,
    Key: file.name,
    Body: file,
  };
  const upload = new Upload({
    params,
    client: s3,
  });
  await upload.done();

  return new Response("File uploaded successfully.");
}
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the imports. We'll use the extra ones below.

```ts title="main.ts"
import { Resource } from "sst";
import {
  S3Client,
  GetObjectCommand,
  ListObjectsV2Command,
} from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const s3 = new S3Client();
```

And install the npm packages.

```bash
deno install npm:sst npm:@aws-sdk/client-s3 npm:@aws-sdk/lib-storage npm:@aws-sdk/s3-request-presigner
```

---

## 5. Download the file

We'll add a `/latest` route that'll download the latest file in our S3 bucket. Let's add this below our upload route in `main.ts`.

```ts title="main.ts"
if (url.pathname === "/latest" && req.method === "GET") {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );
  const latestFile = objects.Contents!.sort(
    (a, b) =>
      (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0),
  )[0];
  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });
  return Response.redirect(await getSignedUrl(s3, command));
}
```

---

#### Test your app

To upload a file run the following from your project root. You might have to go to the `MyService` tab in the sidebar and accept the Deno permission prompts.

```bash
curl -F file=@deno.json http://localhost:8000/
```

This should upload the `deno.json`. Now head over to `http://localhost:8000/latest` in your browser and it'll show you what you just uploaded.

---

## 5. Deploy your app

To deploy our app we'll first add a `Dockerfile`.

```dockerfile title="Dockerfile"
FROM denoland/deno

EXPOSE 8000

USER deno

WORKDIR /app

ADD . /app

RUN deno install --entrypoint main.ts

CMD ["run", "--allow-all",  "main.ts"]
```

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Now to build our Docker image and deploy we run:

```bash
sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production. This'll give the URL of your Deno app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://prod-MyServiceLoadBalanc-491430065.us-east-1.elb.amazonaws.com
```

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Drizzle (`www/src/content/docs/docs/start/aws/drizzle.mdx`)

---
title: Drizzle with Amazon RDS and SST
description: Use Drizzle and SST to manage and deploy your Amazon Postgres RDS database.
---

You can use SST to deploy an Amazon Postgres RDS database and set up [Drizzle ORM](https://orm.drizzle.team) and [Drizzle Kit](https://orm.drizzle.team/docs/kit-overview) to manage it.


:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-drizzle) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

#### Examples

We also have a few other Drizzle and Postgres examples that you can refer to.

- [Run migrations in your CI/CD pipeline](/docs/examples/#drizzle-migrations-in-cicd)
- [Run Postgres in a local Docker container for dev](/docs/examples/#aws-postgres-local)
- [Use Next.js, Postgres, and Drizzle with the T3 Stack](/docs/examples/#t3-stack-in-aws)

---

## 1. Create a project

Let's start by creating a Node.js app.

```bash
mkdir aws-drizzle && cd aws-drizzle
npm init -y
```

---

#### 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.

---

#### Init Drizzle

Add Drizzle to your project. We're also adding the `pg` client that Drizzle will use.

```bash
npm install pg @types/pg drizzle-orm drizzle-kit
```

Drizzle ORM is what will be used to query our database, while Drizzle Kit will allow us to run migrations. It also comes with Drizzle Studio, a query browser.

Let's add the following to the `scripts` in the `package.json`.

```json title="package.json" "sst shell"
"scripts": {
  "db": "sst shell drizzle-kit"
},
```

The `sst shell` CLI will pass the credentials to Drizzle Kit and allow it to connect to your database.

Let's also update our `tsconfig.json`.

```json title="tsconfig.json"
{
  "compilerOptions": {
    "strict": true
  }
}
```

---

## 2. Add a Postgres db

Let's add a Postgres database using [Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html). This needs a VPC. Update your `sst.config.ts`.

```ts title="sst.config.ts"
async run() {
  const vpc = new sst.aws.Vpc("MyVpc", { bastion: true, nat: "ec2" });
  const rds = new sst.aws.Postgres("MyPostgres", { vpc, proxy: true });
},
```

The `proxy` option configures an RDS Proxy behind the scenes making it ideal for serverless applications.

:::tip
The RDS Proxy allows serverless environments to reliably connect to RDS.
:::

While the `bastion` option will let us connect to the VPC from our local machine. We also need the NAT gateway for this example since we'll be using a Lambda function, and this allows a Lambda function that's in a VPC to access the internet.

---

#### Start Drizzle Studio

When you run SST in dev it can start other dev processes for you. In this case we want to start Drizzle Studio. Add this below the `Postgres` component.

```ts title="sst.config.ts"
new sst.x.DevCommand("Studio", {
  link: [rds],
  dev: {
    command: "npx drizzle-kit studio",
  },
});
```

This will run the given command in dev.

---

#### Add an API

We'll use a Lambda function as an API to query our database. Add the following to your `sst.config.ts` below the database config.

```ts title="sst.config.ts" {4}
new sst.aws.Function("MyApi", {
  vpc,
  url: true,
  link: [rds],
  handler: "src/api.handler",
});
```

We are linking our database to the API.

---

#### Install a tunnel

Since our database cluster is in a VPC, we'll need a tunnel to connect to it from our local machine.

```bash "sudo"
sudo npx sst tunnel install
```

This needs _sudo_ to create a network interface on your machine. You'll only need to do this once on your machine.

---

#### Start dev mode

Start your app in dev mode. This runs your functions [_Live_](/docs/live/).

```bash
npx sst dev
```

It'll take a few minutes to create your database. Once complete, you'll see this.

```bash frame="none"
✓  Complete
   MyApi: https://ouu5vovpxllyn5b6ot2nn6vdsa0hvcuj.lambda-url.us-east-1.on.aws
```

You'll see Drizzle Studio started in a tab called **Studio**. And a tunnel in the **Tunnel** tab.

---

## 3. Create a schema

Let's define our Drizzle config. Add a `drizzle.config.ts` in your project root with this.

```ts title="drizzle.config.ts" {6-8}
import { Resource } from "sst";
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  dialect: "postgresql",
  // Pick up all our schema files
  schema: ["./src/**/*.sql.ts"],
  out: "./migrations",
  dbCredentials: {
    host: Resource.MyPostgres.host,
    port: Resource.MyPostgres.port,
    user: Resource.MyPostgres.username,
    password: Resource.MyPostgres.password,
    database: Resource.MyPostgres.database,
  },
});
```

Here we are telling Drizzle that we'll be specifying your database schema in `.sql.ts` files in our `src/` directory.

:::tip
SST allows us to automatically access our database with `Resource.MyPostgres.*`.
:::

We are going to create a simple database to store some todos. Create a new file in `src/todo.sql.ts` with the following.

```ts title="src/todo.sql.ts"
import { text, serial, pgTable } from "drizzle-orm/pg-core";

export const todo = pgTable("todo", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  description: text("description"),
});
```

---

## 4. Generate a migration

We can use this to generate a migration.

```bash
npm run db generate
```

This in turn runs `sst shell drizzle-kit generate` and creates a new migration in the `migrations/` directory.

---

#### Apply the migration

Now we can apply our migration using.

```bash
npm run db migrate
```

This should create our new schema.

:::tip
You need a tunnel to connect to your database.
:::

This needs the tunnel to connect to the database. So you should have `sst dev` in a separate terminal.

```bash "sudo"
npx sst tunnel
```

Alternatively, you can just run the tunnel using the above command.

---

#### Drizzle Studio

To see our schema in action we can open the Drizzle Studio. Head over to the **Studio** tab in your `sst dev` session and go to the link.

Or head over to `https://local.drizzle.studio` in your browser.




---

## 5. Query the database

To use Drizzle ORM to query our database, create a new `src/drizzle.ts` config file with the following.

```ts title="src/drizzle.ts"
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { Resource } from "sst";
import * as schema from "./todo.sql";

const pool = new Pool({
  host: Resource.MyPostgres.host,
  port: Resource.MyPostgres.port,
  user: Resource.MyPostgres.username,
  password: Resource.MyPostgres.password,
  database: Resource.MyPostgres.database,
});

export const db = drizzle(pool, { schema });
```

Now we can use that in the API. Create our API handler in `src/api.ts`.

```ts title="src/api.ts"
import { db } from "./drizzle";
import { todo } from "./todo.sql";
import { APIGatewayProxyEventV2 } from "aws-lambda";

export const handler = async (evt: APIGatewayProxyEventV2) => {
  if (evt.requestContext.http.method === "GET") {
    const result = await db.select().from(todo).execute();

    return {
      statusCode: 200,
      body: JSON.stringify(result, null, 2),
    };
  }

  if (evt.requestContext.http.method === "POST") {
    const result = await db
      .insert(todo)
      .values({ title: "Todo", description: crypto.randomUUID() })
      .returning()
      .execute();

    return {
      statusCode: 200,
      body: JSON.stringify(result),
    };
  }
};
```

For _POST_ requests we create a new todo and for _GET_ requests we simply print out all our todos.

---

#### Test your app

To test our app, make a _POST_ request to our API.

```bash
curl -X POST https://ouu5vovpxllyn5b6ot2nn6vdsa0hvcuj.lambda-url.us-east-1.on.aws
```

Now if you head over to `https://ouu5vovpxllyn5b6ot2nn6vdsa0hvcuj.lambda-url.us-east-1.on.aws` in your browser, you'll see that a todo has been added.



You should see this in the Drizzle Studio as well.

---

## 6. Deploy your app

Finally, let's deploy your app.

```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](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Email (`www/src/content/docs/docs/start/aws/email.mdx`)

---
title: Send emails in AWS with SST
description: Send emails from your API in AWS with SST.
---

We are going to build a simple SST app in AWS with a serverless API, and send emails from it.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-email) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

## 1. Create a project

Let's start by creating our app.

```bash
mkdir my-email-app && cd my-email-app
npm init -y
```

#### 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.

---

## 2. Add email

Let's add Email to our app, it uses [Amazon SES](https://aws.amazon.com/ses/) behind the scenes. Update your `sst.config.ts`.

```js title="sst.config.ts" {3}
async run() {
  const email = new sst.aws.Email("MyEmail", {
    sender: "email@example.com",
  });
}
```

SES can send emails from a verified email address or domain. To keep things simple we'll be sending from an email. Make sure to use your email address here as we'll be verifying it in the next step.

---

## 3. Add an API

Next let's create a simple API that'll send out an email when invoked. Add this to your `sst.config.ts`.

```js title="sst.config.ts" {3}
const api = new sst.aws.Function("MyApi", {
  handler: "sender.handler",
  link: [email],
  url: true,
});

return {
  api: api.url,
};
```

We are linking the our email component to our API.

---

#### Start dev mode

Start your app in dev mode. This runs your functions [_Live_](/docs/live/).

```bash
npx sst dev
```

This will give you your API URL.

```bash frame="none"
+  Complete
   api: https://wwwrwteda6kbpquppdz5i3lg4a0nvmbf.lambda-url.us-east-1.on.aws/
```

You should also get an email asking you to verify the sender email address.



Click the link to verify your email address. 

---

## 4. Send an email

We'll use the SES client to send an email when the API is invoked. Create a new `sender.ts` file and add the following to it.

```ts title="sender.ts" {4}
export const handler = async () => {
  await client.send(
    new SendEmailCommand({
      FromEmailAddress: Resource.MyEmail.sender,
      Destination: {
        ToAddresses: [Resource.MyEmail.sender],
      },
      Content: {
        Simple: {
          Subject: {
            Data: "Hello World!",
          },
          Body: {
            Text: {
              Data: "Sent from my SST app.",
            },
          },
        },
      },
    })
  );

  return {
    statusCode: 200,
    body: "Sent!"
  };
};
```

We are sending an email to the same verified email that we are sending from because your SES account might be in _sandbox_ mode and can only send to verified emails. We'll look at how to go to production below.

:::tip
We are accessing our email service with `Resource.Email.sender`.
:::

Add the imports.

```ts title="sender.ts"
import { Resource } from "sst";
import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";

const client = new SESv2Client();
```

And install the npm packages.

```bash
npm install @aws-sdk/client-sesv2
```

---

#### Test your app

To test our app, hit the API.

```bash
curl https://wwwrwteda6kbpquppdz5i3lg4a0nvmbf.lambda-url.us-east-1.on.aws
```

This should print out `Sent!` and you should get an email. You might have to check your spam folder since the sender and receiver email address is the same in this case.



---

## 5. Deploy your app

Now let's deploy your app.


```bash
npx sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production.

Next, for production you can:

1. [Request production access](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html) for SES
2. And [use your domain](/docs/component/aws/email/) to send emails

This'll let you send emails from your domain to any email address.

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Express (`www/src/content/docs/docs/start/aws/express.mdx`)

---
title: Express on AWS with SST
description: Create and deploy an Express app to AWS with SST.
---

We are going to build an app with Express, 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](https://github.com/sst/sst/tree/dev/examples/aws-express) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

#### Examples

We also have a few other Express examples that you can refer to.

- [Build a hit counter with Express and Redis](/docs/examples/#aws-express-redis)
- [Use service discovery to connect to your Express app](/docs/examples/#aws-cluster-service-discovery)

---

## 1. Create a project

Let's start by creating our Express app.

```bash
mkdir aws-express && cd aws-express
npm init -y
npm install express
```

---

#### Init Express

Create your app by adding an `index.mjs` to the root.

```js title="index.mjs"
import express from "express";

const PORT = 80;

const app = express();

app.get("/", (req, res) => {
  res.send("Hello World!")
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
```

---

#### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

This'll create a `sst.config.ts` file in your project root.

---

## 2. Add a Service

To deploy our Express app, let's add an [AWS Fargate](https://aws.amazon.com/fargate/) container with [Amazon ECS](https://aws.amazon.com/ecs/). Update your `sst.config.ts`.

```js 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" }],
    },
    dev: {
      command: "node --watch index.mjs",
    },
  });
}
```

This creates a VPC with an ECS Cluster, and adds a Fargate service to it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Express app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Express app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your Express app in your browser.

---

## 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Add this to your `sst.config.ts` below the `Vpc` component.

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

---

#### Link the bucket

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 Express app.

---

## 4. Upload a file

We want a `POST` request made to the `/` route to upload a file to our S3 bucket. Let's add this below our _Hello World_ route in our `index.mjs`.

```js title="index.mjs" {4}
app.post("/", upload.single("file"), async (req, res) => {
  const file = req.file;
  const params = {
    Bucket: Resource.MyBucket.name,
    ContentType: file.mimetype,
    Key: file.originalname,
    Body: file.buffer,
  };

  const upload = new Upload({
    params,
    client: s3,
  });

  await upload.done();

  res.status(200).send("File uploaded successfully.");
});
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the imports. We'll use the extra ones below.

```js title="index.mjs"
import multer from "multer";
import { Resource } from "sst";
import { Upload } from "@aws-sdk/lib-storage";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import {
  S3Client,
  GetObjectCommand,
  ListObjectsV2Command,
} from "@aws-sdk/client-s3";

const s3 = new S3Client({});
const upload = multer({ storage: multer.memoryStorage() });
```

And install the npm packages.

```bash
npm install multer @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
```

---

## 5. Download the file

We'll add a `/latest` route that'll download the latest file in our S3 bucket. Let's add this below our upload route in `index.mjs`.

```js title="index.mjs"
app.get("/latest", async (req, res) => {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );

  const latestFile = objects.Contents.sort(
    (a, b) => b.LastModified - a.LastModified,
  )[0];

  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });
  const url = await getSignedUrl(s3, command);

  res.redirect(url);
});
```

---

#### Test your app

To upload a file run the following from your project root.

```bash
curl -F file=@package.json http://localhost:80/
```

This should upload the `package.json`. Now head over to `http://localhost:80/latest` in your browser and it'll show you what you just uploaded.

---

## 5. Deploy your app

To deploy our app we'll first add a `Dockerfile`.

```dockerfile title="Dockerfile"
FROM node:lts-alpine

WORKDIR /app/

COPY package.json /app
RUN npm install

COPY index.mjs /app

ENTRYPOINT ["node", "index.mjs"]
```

This just builds our Express app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
```

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. This'll give the URL of your Express app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://jayair-MyServiceLoadBala-592628062.us-east-1.elb.amazonaws.com
```

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Hono (`www/src/content/docs/docs/start/aws/hono.mdx`)

---
title: Hono on AWS with SST
description: Create and deploy a Hono API in AWS with SST.
---

There are two ways to deploy a [Hono](https://hono.dev) app to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#containers)

We'll use both to build a couple of simple apps below.

---

#### Examples

We also have a few other Hono examples that you can refer to.

- [Enabling streaming in your Hono app](/docs/examples/#aws-hono-streaming)
- [Hit counter with Redis and Hono in a container](/docs/examples/#aws-hono-container-with-redis)

---

## Serverless

We are going to build a serverless Hono API, add an S3 Bucket for file uploads, and deploy it using a Lambda function.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-hono) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npm create hono@latest aws-hono
cd aws-hono
```

We are picking the **aws-lambda** template.

##### 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.

---

### 2. Add an API

Let's add a Hono API using an AWS Lambda. Update your `sst.config.ts`.

```js title="sst.config.ts"
async run() {
  new sst.aws.Function("Hono", {
    url: true,
    handler: "src/index.handler",
  });
}
```

We are enabling the function URL for this.

---

##### Start dev mode

Start your app in dev mode. This runs your functions [_Live_](/docs/live/).

```bash
npx sst dev
```

This will give you the URL of your API.

```bash frame="none"
✓  Complete
   Hono: https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws
```

---

### 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Update your `sst.config.ts`.

```js title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket");
```

##### Link the bucket

Now, link the bucket to the API.

```ts title="sst.config.ts" {3}
new sst.aws.Function("Hono", {
  url: true,
  link: [bucket],
  handler: "src/index.handler",
});
```

---

### 4. Upload a file

We want the `/` route of our API to generate a pre-signed URL to upload a file to our S3 Bucket. Replace the _Hello Hono_ route in `src/index.ts`.

```ts title="src/index.ts" {4}
app.get('/', async (c) => {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });

  return c.text(await getSignedUrl(s3, command));
});
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Then add the relevant imports. We'll use the extra ones below.

```ts title="src/index.ts"
import { Resource } from 'sst'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import {
  S3Client,
  GetObjectCommand,
  PutObjectCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3'

const s3 = new S3Client();
```

---

### 5. Download a file

We want the `/latest` route of our API to generate a pre-signed URL to download the last uploaded file in our S3 Bucket. Add this to your routes in `src/index.ts`.

```ts title="src/index.ts"
app.get('/latest', async (c) => {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );

  const latestFile = objects.Contents!.sort(
    (a, b) =>
      (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0),
  )[0];

  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });

  return c.redirect(await getSignedUrl(s3, command));
});
```

---

##### Test your app

Let's try uploading a file from your project root. Make sure to use your API URL.

```bash
curl --upload-file package.json "$(curl https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws)"
```

Now head over to `https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws/latest` in your browser and it'll download the file you just uploaded.

---

### 6. Deploy your app

Now let's deploy your app.

```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 Hono API, 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](https://github.com/sst/sst/tree/dev/examples/aws-hono-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npm create hono@latest aws-hono-container
cd aws-hono-container
```

We are picking the **nodejs** template.

##### 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.

---

### 2. Add a Service

To deploy our Hono app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in


```js 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: "3000/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 Hono app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Hono app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your Hono app in your browser.

---

### 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Add this to your `sst.config.ts` below the `Vpc` component.

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

---

##### Link the bucket

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 Hono app.

---

### 4. Upload a file

We want a `POST` request made to the `/` route to upload a file to our S3 bucket. Let's add this below our _Hello Hono_ route in our `src/index.ts`.

```ts title="src/index.ts" {6}
app.post('/', async (c) => {
  const body = await c.req.parseBody();
  const file = body['file'] as File;

  const params = {
    Bucket: Resource.MyBucket.name,
    ContentType: file.type,
    Key: file.name,
    Body: file,
  };
  const upload = new Upload({
    params,
    client: s3,
  });
  await upload.done();

  return c.text('File uploaded successfully.');
});
```

Add the imports. We'll use the extra ones below.

```tsx title="src/index.ts"
import { Resource } from 'sst'
import {
  S3Client,
  GetObjectCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3'
import { Upload } from '@aws-sdk/lib-storage'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const s3 = new S3Client();
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
```

---

### 5. Download the file

We'll add a `/latest` route that'll download the latest file in our S3 bucket. Let's add this below our upload route in `src/index.ts`.

```ts title="src/index.ts"
app.get('/latest', async (c) => {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );
  const latestFile = objects.Contents!.sort(
    (a, b) =>
      (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0),
  )[0];
  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });
  return c.redirect(await getSignedUrl(s3, command));
});
```

---

#### Test your app

To upload a file run the following from your project root.

```bash
curl -F file=@package.json http://localhost:3000/
```

This should upload the `package.json`. Now head over to `http://localhost:3000/latest` in your browser and it'll show you what you just uploaded.

---

### 6. Deploy your app

To deploy our app we'll first add a `Dockerfile`. This is building our app by running our `build` script from above.

```diff lang="dockerfile" title="Dockerfile"
FROM node:lts-alpine AS base

FROM base AS builder
RUN apk add --no-cache gcompat
WORKDIR /app
COPY package*json tsconfig.json src ./
+ # Copy over generated types
+ COPY sst-env.d.ts* ./
RUN npm ci && \
  npm run build && \
  npm prune --production

FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono
COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules
COPY --from=builder --chown=hono:nodejs /app/dist /app/dist
COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json

USER hono
EXPOSE 3000
CMD ["node", "/app/dist/index.js"]
```

This builds our Hono app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
.git
```

To compile our TypeScript file, we'll need add the following to the `tsconfig.json`.

```diff lang="json" title="tsconfig.json" {4,6}
{
  "compilerOptions": {
    // ...
+    "outDir": "./dist"
  },
+  "exclude": ["node_modules"]
}
```

Install TypeScript.

```bash
npm install typescript --save-dev
```

And add a `build` script to our `package.json`.

```diff lang="json" title="package.json"
"scripts": {
  // ...
+  "build": "tsc"
}
```

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. This'll give the URL of your Hono app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://prod-MyServiceLoadBalanc-491430065.us-east-1.elb.amazonaws.com
```

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Nestjs (`www/src/content/docs/docs/start/aws/nestjs.mdx`)

---
title: NestJS on AWS with SST
description: Create and deploy an NestJS app to AWS with SST.
---

We are going to build an app with NestJS, 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](https://github.com/sst/sst/tree/dev/examples/aws-nestjs-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

:::note
You need Node 22.12 or higher for this example to work.
:::

Also make sure you have Node 22.12. Or set the `--experimental-require-module` flag. This'll allow NestJS to import the SST SDK.

---

#### Examples

We also have a few other NestJS examples that you can refer to.

- [Build a hit counter with NestJS and Redis](/docs/examples/#aws-nestjs-with-redis)

---

## 1. Create a project

Let's start by creating our Nest app.

```bash
nest new aws-nestjs-container
cd aws-nestjs-container
```

We are picking npm as the package manager.

---

#### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

This'll create a `sst.config.ts` file in your project root.

To make sure the types in the `sst.config.ts` are picked up, add the following to the `tsconfig.json`.

```diff lang="json" title="tsconfig.json"
{
+ "include": ["src/**/*", "test/**/*", "sst-env.d.ts"]
}
```

---

## 2. Add a Service

To deploy our Nest app, let's add an [AWS Fargate](https://aws.amazon.com/fargate/) container with [Amazon ECS](https://aws.amazon.com/ecs/). Update your `sst.config.ts`.

```js 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: "3000/http" }],
    },
    dev: {
      command: "npm run start:dev",
    },
  });
}
```

This creates a VPC with an ECS Cluster, and adds a Fargate service to it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Nest app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Nest app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your Nest app in your browser.

---

## 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Add this to your `sst.config.ts` below the `Vpc` component.

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

---

#### Link the bucket

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 Nest app.

---

## 4. Upload a file

We want a `POST` request made to the `/` route to upload a file to our S3 bucket. Let's add this below our `getHello` method in our `src/app.controller.ts`.

```ts title="src/app.controller.ts" {5}
@Post()
@UseInterceptors(FileInterceptor('file'))
async uploadFile(@UploadedFile() file: Express.Multer.File): Promise<string> {
  const params = {
    Bucket: Resource.MyBucket.name,
    ContentType: file.mimetype,
    Key: file.originalname,
    Body: file.buffer,
  };

  const upload = new Upload({
    params,
    client: s3,
  });

  await upload.done();

  return 'File uploaded successfully.';
}
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the imports. We'll use the extra ones below.

```ts title="src/app.controller.ts"
import {
  S3Client,
  GetObjectCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3';
import { Resource } from 'sst';
import { Express } from 'express';
import { Upload } from '@aws-sdk/lib-storage';
import { FileInterceptor } from '@nestjs/platform-express';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { Post, Redirect, UploadedFile, UseInterceptors } from '@nestjs/common';

const s3 = new S3Client({});
```

And install the npm packages.

```bash
npm install -D @types/multer
npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
```

---

## 5. Download the file

We'll add a `/latest` route that'll download the latest file in our S3 bucket. Let's add this below our `uploadFile` method in `src/app.controller.ts`.

```ts title="src/app.controller.ts"
@Get('latest')
@Redirect('/', 302)
async getLatestFile() {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );

  const latestFile = objects.Contents.sort(
    (a, b) => b.LastModified.getTime() - a.LastModified.getTime(),
  )[0];

  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });
  const url = await getSignedUrl(s3, command);

  return { url };
}
```

---

#### Test your app

To upload a file run the following from your project root.

```bash
curl -F file=@package.json http://localhost:3000/
```

This should upload the `package.json`. Now head over to `http://localhost:3000/latest` in your browser and it'll download you what you just uploaded.

---

## 5. Deploy your app

To deploy our app we'll first add a `Dockerfile`.

```dockerfile title="Dockerfile"
FROM node:22

WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

EXPOSE 3000
CMD ["node", "dist/main"]
```

This just builds our Nest app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
dist
node_modules
```

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. This'll give the URL of your Nest app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://jayair-MyServiceLoadBala-592628062.us-east-1.elb.amazonaws.com
```

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Nextjs (`www/src/content/docs/docs/start/aws/nextjs.mdx`)

---
title: Next.js on AWS with SST
description: Create and deploy a Next.js app to AWS with SST.
---

import { Image } from "astro:assets"

import nextApp from "../../../../../assets/docs/start/start-nextjs-local.png"
import nextAppContainer from "../../../../../assets/docs/start/start-nextjs-local-container.png"
import consoleAutodeploy from "../../../../../assets/docs/start/sst-console-autodeploy.png"

There are two ways to deploy a Next.js app to AWS with SST.

1. [Serverless with OpenNext](#serverless)
2. [Containers with Docker](#containers)

We'll use both to build a couple of simple apps below.

---

#### Examples

We also have a few other Next.js examples that you can refer to.

- [Adding basic auth to your Next.js app](/docs/examples/#aws-nextjs-basic-auth)
- [Enabling streaming in your Next.js app](/docs/examples/#aws-nextjs-streaming)
- [Add additional routes to the Next.js CDN](/docs/examples/#aws-nextjs-add-behavior)
- [Hit counter with Redis and Next.js in a container](/docs/examples/#aws-nextjs-container-with-redis)

---

## Serverless

We are going to create a Next.js app, add an S3 Bucket for file uploads, and deploy it using [OpenNext](https://opennext.js.org) and the `Nextjs` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-nextjs) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npx create-next-app@latest aws-nextjs
cd aws-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.

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your Next.js app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your Next.js 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 `Nextjs` component.

##### Link the bucket

Now, link the bucket to our Next.js app.

```js title="sst.config.ts" {2}
new sst.aws.Nextjs("MyWeb", {
  link: [bucket]
});
```

---

### 3. Create an upload form

Add a form client component in `components/form.tsx`.

```tsx title="components/form.tsx"
"use client";

import styles from "./form.module.css";

export default function Form({ url }: { url: string }) {
  return (
    <form
      className={styles.form}
      onSubmit={async (e) => {
        e.preventDefault();

        const file = (e.target as HTMLFormElement).file.files?.[0] ?? null;

        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];
      }}
    >
      <input name="file" type="file" accept="image/png, image/jpeg" />
      <button type="submit">Upload</button>
    </form>
  );
}
```

Add some styles.

```css title="components/form.module.css"
.form {
  padding: 2rem;
  border-radius: 0.5rem;
  background-color: var(--gray-alpha-100);
}

.form input {
  margin-right: 1rem;
}

.form button {
  appearance: none;
  padding: 0.5rem 0.75rem;
  font-weight: 500;
  font-size: 0.875rem;
  border-radius: 0.375rem;
  background-color: transparent;
  font-family: var(--font-geist-sans);
  border: 1px solid var(--gray-alpha-200);
}

.form button:active:enabled {
  background-color: var(--gray-alpha-200);
}
```

---

### 4. Generate a pre-signed URL

When our app loads, we'll generate a pre-signed URL for the file upload and render the form with it. Replace your `Home` component in `app/page.tsx`.

```ts title="app/page.tsx" {6}
export const dynamic = "force-dynamic";

export default async function Home() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });
  const url = await getSignedUrl(new S3Client({}), command);

  return (
    <div className={styles.page}>
      <main className={styles.main}>
        <Form url={url} />
      </main>
    </div>
  );
}
```

We need the `force-dynamic` because we don't want Next.js to cache the pre-signed URL.

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the relevant imports.

```ts title="app/page.tsx"
import { Resource } from "sst";
import Form from "@/components/form";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

---

#### Test your app

Head over to the local Next.js app in your browser, `http://localhost:3000` and try **uploading an image**. You should see it upload and then download the image.

<Image src={nextApp} alt="SST Next.js app local" />

---

### 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.

Congrats! Your app should now be live!

---

## Containers

We are going to create a Next.js app, 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](https://github.com/sst/sst/tree/dev/examples/aws-nextjs-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npx create-next-app@latest aws-nextjs-container
cd aws-nextjs-container
```

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 a Service

To deploy our Next.js app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in your `sst.config.ts`.

```js 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: "3000/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 is not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Next.js app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Next.js app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your Next.js app 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.

---

##### Link the bucket

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 Next.js app.

---

### 4. Create an upload form

Add a form client component in `components/form.tsx`.

```tsx title="components/form.tsx"
"use client";

import styles from "./form.module.css";

export default function Form({ url }: { url: string }) {
  return (
    <form
      className={styles.form}
      onSubmit={async (e) => {
        e.preventDefault();

        const file = (e.target as HTMLFormElement).file.files?.[0] ?? null;

        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];
      }}
    >
      <input name="file" type="file" accept="image/png, image/jpeg" />
      <button type="submit">Upload</button>
    </form>
  );
}
```

Add some styles.

```css title="components/form.module.css"
.form {
  padding: 2rem;
  border-radius: 0.5rem;
  background-color: var(--gray-alpha-100);
}

.form input {
  margin-right: 1rem;
}

.form button {
  appearance: none;
  padding: 0.5rem 0.75rem;
  font-weight: 500;
  font-size: 0.875rem;
  border-radius: 0.375rem;
  background-color: transparent;
  font-family: var(--font-geist-sans);
  border: 1px solid var(--gray-alpha-200);
}

.form button:active:enabled {
  background-color: var(--gray-alpha-200);
}
```

---

### 5. Generate a pre-signed URL

When our app loads, we'll generate a pre-signed URL for the file upload and render the form with it. Replace your `Home` component in `app/page.tsx`.

```ts title="app/page.tsx" {6}
export const dynamic = "force-dynamic";

export default async function Home() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });
  const url = await getSignedUrl(new S3Client({}), command);

  return (
    <div className={styles.page}>
      <main className={styles.main}>
        <Form url={url} />
      </main>
    </div>
  );
}
```

We need the `force-dynamic` because we don't want Next.js to cache the pre-signed URL.

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the relevant imports.

```ts title="app/page.tsx"
import { Resource } from "sst";
import Form from "@/components/form";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

---

#### Test your app

Head over to the local Next.js app in your browser, `http://localhost:3000` and try **uploading an image**. You should see it upload and then download the image.

<Image src={nextAppContainer} alt="SST Next.js app local" />

---

### 6. Deploy your app

To build our app for production, we'll enable Next.js's [standalone output](https://nextjs.org/docs/pages/api-reference/next-config-js/output#automatically-copying-traced-files). Let's update our `next.config.mjs`.

```js title="next.config.ts" {3}
const nextConfig: NextConfig = {
  /* config options here */
  output: "standalone"
};
```

Now to deploy our app we'll add a `Dockerfile`.

```dockerfile title="Dockerfile"
FROM node:lts-alpine AS base

# Stage 1: Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
COPY sst-env.d.ts* ./
RUN npm ci

# Stage 2: Build the application
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# If static pages do not need linked resources
RUN npm run build

# If static pages need linked resources
# RUN --mount=type=secret,id=SST_RESOURCE_MyResource,env=SST_RESOURCE_MyResource \
#   npm run build

# Stage 3: Production server
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]
```

This builds our Next.js app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

If your Next.js app is building static pages that need linked resources, you can need to declare them in your `Dockerfile`. For example, if we need the linked `MyBucket` component from above.

```dockerfile
RUN --mount=type=secret,id=SST_RESOURCE_MyBucket,env=SST_RESOURCE_MyBucket npm run build
```

You'll need to do this for each linked resource.

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
.git
.next
node_modules
```

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](/docs/console/) to _**git push to deploy**_ your app and view logs from it.

<Image src={consoleAutodeploy} alt="SST Console Autodeploy" />

You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Nuxt (`www/src/content/docs/docs/start/aws/nuxt.mdx`)

---
title: Nuxt on AWS with SST
description: Create and deploy a Nuxt app to AWS with SST.
---

There are two ways to deploy a Nuxt app to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#containers)

We'll use both to build a couple of simple apps below.

---

## Serverless

We are going to create a Nuxt app, add an S3 Bucket for file uploads, and deploy it using the `Nuxt` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-nuxt) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npx nuxi@latest init aws-nuxt
cd aws-nuxt
```

We are picking the **npm** as the package manager.

---

##### 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 `nuxt.config.ts` with something like this.

```diff lang="ts" title="nuxt.config.ts"
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
+  nitro: {
+    preset: 'aws-lambda'
+  },
  devtools: { enabled: true }
})
```

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your Nuxt app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your Nuxt 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`.

```ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
  access: "public"
});
```

Add this above the `Nuxt` component.

##### Link the bucket

Now, link the bucket to our Nuxt app.

```ts title="sst.config.ts" {2}
new sst.aws.Nuxt("MyWeb", {
  link: [bucket],
});
```

---

### 3. Generate a pre-signed URL

When our app loads, we'll call an API that'll generate a pre-signed URL for the file upload. Create a new `server/api/presigned.ts` with the following.

```tsx title="server/api/presigned.ts" {4}
export default defineEventHandler(async () => {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });

  return await getSignedUrl(new S3Client({}), command);
})
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the relevant imports.

```tsx title="src/app.tsx"
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

---

### 4. Create an upload form

Add a form to upload files to the presigned URL. Replace our `app.vue` with:

```vue title="app.vue"
<script setup>
  const file = ref(null);
  const { data } = await useFetch('/api/presigned');

  async function onSubmit() {
    const upload = file.value.files[0];
    const image = await fetch(data.value, {
      body: upload,
      method: "PUT",
      headers: {
        "Content-Type": upload.type,
        "Content-Disposition": `attachment; filename="${upload.name}"`,
      },
    });

    window.location.href = image.url.split("?")[0];
  }
</script>
<template>
  <form novalidate @submit.prevent="onSubmit">
    <input type="file" ref="file" accept="image/png, image/jpeg" />
    <button type="submit">Upload</button>
  </form>
</template>
```

Head over to the local app in your browser, `http://localhost:3000` 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.

Congrats! Your site should now be live!



---

## Containers

We are going to build a hit counter Nuxt app with Redis. We'll deploy it to AWS in a container using the `Cluster` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-nuxt-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npx nuxi@latest init aws-nuxt-container
cd aws-nuxt-container
```

We are picking the **npm** as the package manager.

---

##### 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 `nuxt.config.ts`. But instead we'll use the **default Node preset**.

```ts title="nuxt.config.ts"
export default defineNuxtConfig({
  compatibilityDate: '2024-11-01',
  devtools: { enabled: true }
})
```

---

### 2. Add a Cluster

To deploy our Nuxt app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in your `sst.config.ts`.

```js title="sst.config.ts" {10-12}
async run() {
  const vpc = new sst.aws.Vpc("MyVpc", { bastion: true });
  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",
    },
  });
}
```

This creates a VPC with a bastion host, an ECS Cluster, and adds a Fargate service to it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Nuxt app locally in dev mode.

---

### 3. Add Redis

Let's add an [Amazon ElastiCache](https://aws.amazon.com/elasticache/) Redis cluster. Add this below the `Vpc` component in your `sst.config.ts`.

```js title="sst.config.ts"
const redis = new sst.aws.Redis("MyRedis", { vpc });
```

This shares the same VPC as our ECS cluster.

---

#### Link Redis

Now, link the Redis cluster to the container.

```ts title="sst.config.ts" {3}
new sst.aws.Service("MyService", {
  // ...
  link: [redis],
});
```

This will allow us to reference the Redis cluster in our Nuxt app.

---

#### Install a tunnel

Since our Redis cluster is in a VPC, we'll need a tunnel to connect to it from our local machine.

```bash "sudo"
sudo npx sst tunnel install
```

This needs _sudo_ to create a network interface on your machine. You'll only need to do this once on your machine.

---

#### Start dev mode

Start your app in dev mode.

```bash
npx sst dev
```

This will deploy your app, start a tunnel in the **Tunnel** tab, and run your Nuxt app locally in the **MyServiceDev** tab.

---

### 4. Connect to Redis

We want the `/` route to increment a counter in our Redis cluster. Let's start by installing the npm package we'll use.

```bash
npm install ioredis
```

We'll call an API that'll increment the counter when the app loads. Create a new `server/api/counter.ts` with the following.

```ts title="server/api/counter.ts" {5}
import { Resource } from "sst";
import { Cluster } from "ioredis";

const redis = new Cluster(
  [{ host: Resource.MyRedis.host, port: Resource.MyRedis.port }],
  {
    dnsLookup: (address, callback) => callback(null, address),
    redisOptions: {
      tls: {},
      username: Resource.MyRedis.username,
      password: Resource.MyRedis.password,
    },
  }
);

export default defineEventHandler(async () => {
  return await redis.incr("counter");
})
```

:::tip
We are directly accessing our Redis cluster with `Resource.MyRedis.*`.
:::

Let's update our component to show the counter. Replace our `app.vue` with:

```vue title="app.vue"
<script setup lang="ts">
const { data: counter } = await useFetch("/api/counter")
</script>

<template>
  <p>Hit counter: {{ counter }}</p>
</template>
```

---

#### Test your app

Let's head over to `http://localhost:3000` in your browser and it'll show the current hit counter.

You should see it increment every time you **refresh the page**.

---

### 5. Deploy your app

To deploy our app we'll add a `Dockerfile`.

<details>
<summary>View Dockerfile</summary>

```dockerfile title="Dockerfile"
FROM node:lts AS base

WORKDIR /src

# Build
FROM base as build

COPY --link package.json package-lock.json ./
RUN npm install

COPY --link . .

RUN npm run build

# Run
FROM base

ENV PORT=3000
ENV NODE_ENV=production

COPY --from=build /src/.output /src/.output

CMD [ "node", ".output/server/index.mjs" ]
```

</details>

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
```

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](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Prisma (`www/src/content/docs/docs/start/aws/prisma.mdx`)

---
title: Prisma with Amazon RDS and SST
description: Use Prisma and SST to manage and deploy your Amazon Postgres RDS database.
---

We are going to use Prisma and SST to deploy an Amazon Postgres RDS database and connect to it from an Express app in a container.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-prisma) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

#### Examples

We also have a few other Prisma and Postgres examples that you can refer to.

- [Use Prisma in a Lambda function](/docs/examples/#prisma-in-lambda)
- [Run Postgres in a local Docker container for dev](/docs/examples/#aws-postgres-local)

---

## 1. Create a project

Let's start by creating a Node.js app.

```bash
mkdir aws-prisma && cd aws-prisma
npm init -y
```

We'll install Prisma, TypeScript, and Express.

```bash
npm install prisma typescript ts-node @types/node --save-dev
npm install express
```

Let's initialize TypeScript and Prisma.

```bash
npx tsc --init
npx prisma init
```

This will create a `prisma` directory with a `schema.prisma`.

---

#### Init Express

Create your Express app by adding an `index.mjs` to the root.

```js title="index.mjs"
import express from "express";

const PORT = 80;

const app = express();

app.get("/", (req, res) => {
  res.send("Hello World!")
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
```

---

#### 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.

---

## 2. Add a Postgres db

Let's add a Postgres database using [Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html). This needs a VPC.

```ts title="sst.config.ts" {5}
async run() {
  const vpc = new sst.aws.Vpc("MyVpc", { bastion: true });
  const rds = new sst.aws.Postgres("MyPostgres", { vpc });

  const DATABASE_URL = $interpolate`postgresql://${rds.username}:${rds.password}@${rds.host}:${rds.port}/${rds.database}`;
},
```

The `bastion` option will let us connect to the VPC from our local machine.

We are also building the `DATABASE_URL` variable using the outputs from our RDS database. We'll use this later.

---

#### Start Prisma Studio

When you run SST in dev it can start other dev processes for you. In this case we want to start Prisma Studio. Add this below the `DATABASE_URL` variable.

```ts title="sst.config.ts" {2}
new sst.x.DevCommand("Prisma", {
  environment: { DATABASE_URL },
  dev: {
    autostart: false,
    command: "npx prisma studio",
  },
});
```

This will run the given command in dev.

---

## 3. Add a Cluster

To deploy our Express app, let's add an [AWS Fargate](https://aws.amazon.com/fargate/) container with [Amazon ECS](https://aws.amazon.com/ecs/). Add this at the end of your `sst.config.ts`.

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

new sst.aws.Service("MyService", {
  cluster,
  link: [rds],
  environment: { DATABASE_URL },
  loadBalancer: {
    ports: [{ listen: "80/http" }],
  },
  dev: {
    command: "node --watch index.mjs",
  },
});
```

This uses the same VPC, and adds 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 Express app locally in dev mode.

---

#### Install a tunnel

Since our database cluster is in a VPC, we'll need a tunnel to connect to it from our local machine.

```bash "sudo"
sudo npx sst tunnel install
```

This needs _sudo_ to create a network interface on your machine. You'll only need to do this once on your machine.

---

#### Start dev mode

Start your app in dev mode. This will take a few minutes.

```bash
npx sst dev
```

It'll deploy your app, start a tunnel in the **Tunnel** tab, run your Express app locally in the **MyServiceDev** tab, and have your Prisma Studio in the **Studio** tab.

We are setting Prisma Studio to not auto-start since it pops up a browser window. You can start it by clicking on it and hitting _Enter_.

---

## 4. Create a schema

Let's create a simple schema. Add this to your `schema.prisma`.

```prisma title="schema.prisma"
model User {
  id    Int     @id @default(autoincrement())
  name  String?
  email String  @unique
}
```

---

#### Generate a migration

We'll now generate a migration for this schema and apply it. In a separate terminal run:

```bash
npx sst shell --target Prisma -- npx prisma migrate dev --name init
```

We are wrapping the `prisma migrate dev --name init` command in `sst shell --target Prisma` because we want this command to have access to the `DATABASE_URL` defined in our `sst.config.ts`.

The `Prisma` target is coming from the `new sst.x.DevCommand("Prisma")` component defined above.

:::tip
You need a tunnel to connect to your database.
:::

This needs the tunnel to connect to the database. So you should have `sst dev` in a separate terminal.

```bash "sudo"
npx sst tunnel
```

Alternatively, you can just run the tunnel using the above command.

---

#### Prisma Studio

To see our schema in action we can open the Prisma Studio. Head over to the **Studio** tab in your `sst dev` session and hit enter to start it.



---

## 5. Query the database

Running the `migrate dev` command also installs the Prisma Client in our project. So let's use that to query our database.

Replace the `/` route in your `index.mjs` with.

```ts title="index.mjs"
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

app.get("/", async (_req, res) => {
  const user = await prisma.user.create({
    data: {
      name: "Alice",
      email: `alice-${crypto.randomUUID()}@example.com`
    },
  });
  res.send(JSON.stringify(user));
});
```

---

#### Test your app

Let's head over to `http://localhost:80` in your browser and it'll show you the new user that was created.



You should see this in the Prisma Studio as well.

---

## 5. Deploy your app

To deploy our app we'll first add a `Dockerfile`.

```dockerfile title="Dockerfile"
FROM node:18-bullseye-slim

WORKDIR /app/

COPY package.json index.mjs prisma /app/
RUN npm install

RUN npx prisma generate

ENTRYPOINT ["node", "index.mjs"]
```

This just builds our Express app in a Docker image and runs the `prisma generate` command.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
```

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. This'll give the URL of your Express app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://jayair-MyServiceLoadBala-592628062.us-east-1.elb.amazonaws.com
```

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws React (`www/src/content/docs/docs/start/aws/react.mdx`)

---
title: React Router on AWS with SST
description: Create and deploy a React Router v7 app to AWS with SST.
---

We are going to create a React Router v7 app in _Framework mode_, add an S3 Bucket for file uploads, and deploy it to using the `React` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-react-router) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npx create-react-router@latest aws-react-router
cd aws-react-router
```

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.

```ts title="sst.config.ts"
async run() {
  new sst.aws.React("MyWeb");
}
```

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your React Router app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your React Router 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 `React` component.

##### Link the bucket

Now, link the bucket to our React Router app.

```js title="sst.config.ts" {2}
new sst.aws.React("MyWeb", {
  link: [bucket],
});
```

---

### 3. Create an upload form

Add the upload form client in `app/routes/home.tsx`. Replace the `Home` component with:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### 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 above the `Home` component in `app/routes/home.tsx`.

```tsx title="app/routes/home.tsx" {4}
export async function loader() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    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`.
:::

Add the relevant imports.

```tsx title="app/routes/_index.tsx"
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Head over to the local React Router app 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.

Congrats! Your site should now be live!

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Realtime (`www/src/content/docs/docs/start/aws/realtime.mdx`)

---
title: Realtime apps in AWS with SST
description: Use SST to build and deploy a realtime chat app to AWS.
---

We are going to use SST to build and deploy a realtime chat app on AWS.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-realtime-nextjs) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

## 1. Create a project

Let's start by creating a Node.js app.

```bash
npx create-next-app@latest my-realtime-app
cd my-realtime-app
```

---

#### 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.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Next.js app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your Next.js app in your browser.

---

## 2. Add Realtime

Let's add the `Realtime` component and link it to the Next.js app. Update your `sst.config.ts`.

```js title="sst.config.ts" {7}
async run() {
  const realtime = new sst.aws.Realtime("MyRealtime", {
    authorizer: "authorizer.handler",
  });

  new sst.aws.Nextjs("MyWeb", {
    link: [realtime],
  });
},
```

This component allows us to set up _topics_ that can be subscribed to. The `authorizer` function can be used control who has access to these.

---

#### Add an authorizer

Add the following to a new `authorizer.ts` file in your project root.

```ts title="authorizer.ts"
import { Resource } from "sst";
import { realtime } from "sst/aws/realtime";

export const handler = realtime.authorizer(async (token) => {
  const prefix = `${Resource.App.name}/${Resource.App.stage}`;

  const isValid = token === "PLACEHOLDER_TOKEN";

  return isValid
    ? {
      publish: [`${prefix}/*`],
      subscribe: [`${prefix}/*`],
    }
    : {
      publish: [],
      subscribe: [],
    };
});
```

Here we are saying that a user with a valid token has access to publish and subscribe to any topic namespaced user the app and stage name.

:::tip
Namespace your topics with the app and stage name to keep them unique.
:::

In production, we would validate the given token against our database or auth provider.

---

## 3. Create the chat UI

Now let's create a chat interface in our app. Create a new component in `components/chat.tsx` with the following.

```tsx title="components/chat.tsx" {29-33}
"use client";

import mqtt from "mqtt";
import { useState, useEffect } from "react";
import styles from "./chat.module.css";

export default function Chat(
  { topic, endpoint, authorizer }: { topic: string, endpoint: string, authorizer: string }
) {
  const [messages, setMessages] = useState<string[]>([]);
  const [connection, setConnection] = useState<mqtt.MqttClient | null>(null);

  return (
    <div className={styles.chat}>
      {connection && messages.length > 0 &&
        <div className={styles.messages}>
          {messages.map((msg, i) => (
            <div key={i}>{JSON.parse(msg).message}</div>
          ))}
        </div>
      }
      <form
        className={styles.form}
        onSubmit={async (e) => {
          e.preventDefault();

          const input = (e.target as HTMLFormElement).message;

          connection!.publish(
            topic,
            JSON.stringify({ message: input.value }),
            { qos: 1 }
          );
          input.value = "";
        }}
      >
        <input
          required
          autoFocus
          type="text"
          name="message"
          placeholder={
            connection ? "Ready! Say hello..." : "Connecting..."
          }
        />
        <button type="submit" disabled={connection === null}>Send</button>
      </form>
    </div>
  );
}
```

Here we are going to publish a message that's submitted to the given topic. We'll create the realtime connection below.

Add some styles.

```css title="components/chat.module.css"
.chat {
  gap: 1rem;
  width: 30rem;
  display: flex;
  padding: 1rem;
  flex-direction: column;
  border-radius: var(--border-radius);
  background-color: rgba(var(--callout-rgb), 0.5);
  border: 1px solid rgba(var(--callout-border-rgb), 0.3);
}

.messages {
  padding-bottom: 0.125rem;
  border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.3);
}
.messages > div {
  line-height: 1.1;
  padding-bottom: 0.625rem;
}

.form {
  display: flex;
  gap: 0.625rem;
}
.form input {
  flex: 1;
  font-size: 0.875rem;
  padding: 0.5rem 0.75rem;
  border-radius: calc(1rem - var(--border-radius));
  border: 1px solid rgba(var(--callout-border-rgb), 1);
}
.form button {
  font-weight: 500;
  font-size: 0.875rem;
  padding: 0.5rem 0.75rem;
  border-radius: calc(1rem - var(--border-radius));
  background: linear-gradient(
    to bottom right,
    rgba(var(--tile-start-rgb), 1),
    rgba(var(--tile-end-rgb), 1)
  );
  border: 1px solid rgba(var(--callout-border-rgb), 1);
}
.form button:active:enabled {
  background: linear-gradient(
    to top left,
    rgba(var(--tile-start-rgb), 1),
    rgba(var(--tile-end-rgb), 1)
  );
}
```

Install the npm package.

```bash
npm install mqtt
```

---

#### Add to the page

Let's use the component in our page. Replace the `Home` component in `app/page.tsx`. 

```tsx title="app/page.tsx" {23-25}
import { Resource } from "sst";
import Chat from "@/components/chat";

const topic = "sst-chat";

export default function Home() {
  return (
    <main className={styles.main}>

      <div className={styles.center}>
        <Image
          className={styles.logo}
          src="/next.svg"
          alt="Next.js Logo"
          width={180}
          height={37}
          priority
        />
      </div>

      <div>
        <Chat
          endpoint={Resource.MyRealtime.endpoint}
          authorizer={Resource.MyRealtime.authorizer}
          topic={`${Resource.App.name}/${Resource.App.stage}/${topic}`}
        />
      </div>

    </main>
  );
}
```

:::tip
We are directly accessing our Realtime component with `Resource.MyRealtime.*`.
:::

Here we are going to publish and subscribe to a _topic_ called `sst-chat`, namespaced under the name of the app and the stage our app is deployed to.

---

## 4. Create a connection

When our chat component loads, it'll create a new connection to our realtime service. Add the following below the imports in `components/chat.tsx`.

```ts title="components/chat.tsx"
function createConnection(endpoint: string, authorizer: string) {
  return mqtt.connect(`wss://${endpoint}/mqtt?x-amz-customauthorizer-name=${authorizer}`, {
    protocolVersion: 5,
    manualConnect: true,
    username: "", // Must be empty for the authorizer
    password: "PLACEHOLDER_TOKEN", // Passed as the token to the authorizer
    clientId: `client_${window.crypto.randomUUID()}`,
  });
}
```

We are using a _placeholder_ token here. In production this might be a user's session token.

Now let's subscribe to it and save the messages we receive. Add this to the `Chat` component.

```ts title="components/chat.tsx"
useEffect(() => {
  const connection = createConnection(endpoint, authorizer);

  connection.on("connect", async () => {
    try {
      await connection.subscribeAsync(topic, { qos: 1 });
      setConnection(connection);
    } catch (e) { }
  });
  connection.on("message", (_fullTopic, payload) => {
    const message = new TextDecoder("utf8").decode(new Uint8Array(payload));
    setMessages((prev) => [...prev, message]);
  });
  connection.on("error", console.error);

  connection.connect();

  return () => {
    connection.end();
    setConnection(null);
  };
}, [topic, endpoint, authorizer]);
```

:::note
If you have a new AWS account, you might have to wait a few minutes before your realtime service is ready to go.
:::

Head over to the local Next.js app in your browser, `http://localhost:3000` and try **sending a message**, you should see it appear right away. You can also open a new browser window and see them appear in both places.

---

## 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.

Congrats! Your app should now be live!



Next you can:

- Let users create chat rooms
- Save them to a database
- Only show messages from the right chat rooms

This'll help you build realtime apps for production.

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Remix (`www/src/content/docs/docs/start/aws/remix.mdx`)

---
title: Remix on AWS with SST
description: Create and deploy a Remix app to AWS with SST.
---

There are two ways to deploy a Remix app to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#containers)

We'll use both to build a couple of simple apps below.

---

#### Examples

We also have a few other Remix examples that you can refer to.

- [Enabling streaming in your Remix app](/docs/examples/#aws-remix-streaming)
- [Hit counter with Redis and Remix in a container](/docs/examples/#aws-remix-container-with-redis)

---

## Serverless

We are going to create a Remix app, add an S3 Bucket for file uploads, and deploy it to using the `Remix` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-remix) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npx create-remix@latest aws-remix
cd aws-remix
```

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.

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your Remix app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your Remix 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 `Remix` component.

##### Link the bucket

Now, link the bucket to our Remix app.

```js title="sst.config.ts" {2}
new sst.aws.Remix("MyWeb", {
  link: [bucket],
});
```

---

### 3. Create an upload form

Add the upload form client in `app/routes/_index.tsx`. Replace the `Index` component with:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### 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 above the `Index` component in `app/routes/_index.tsx`.

```tsx title="app/routes/_index.tsx" {4}
export async function loader() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    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`.
:::

Add the relevant imports.

```tsx title="app/routes/_index.tsx"
import { Resource } from "sst";
import { useLoaderData } from "@remix-run/react";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Head over to the local Remix app 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.

Congrats! Your site should now be live!

---

## Containers

We are going to create a Remix app, 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](https://github.com/sst/sst/tree/dev/examples/aws-remix-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npx create-remix@latest aws-remix-container
cd aws-remix-container
```

We are picking all the default options.

---

##### 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 a Service

To deploy our Remix app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in your `sst.config.ts`.

```js 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: "3000/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 Remix app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Remix app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your Remix app 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.

---

##### Link the bucket

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 Remix app.

---

### 4. Create an upload form

Add the upload form client in `app/routes/_index.tsx`. Replace the `Index` component with:

```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### 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 above the `Index` component in `app/routes/_index.tsx`.

```tsx title="app/routes/_index.tsx" {4}
export async function loader() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    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`.
:::

Add the relevant imports.

```tsx title="app/routes/_index.tsx"
import { Resource } from "sst";
import { useLoaderData } from "@remix-run/react";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Head over to the local Remix app in your browser, `http://localhost:5173` 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-alpine as base
ENV NODE_ENV production

# Stage 1: Install all node_modules, including dev dependencies
FROM base as deps
WORKDIR /myapp
ADD package.json ./
RUN npm install --include=dev

# Stage 2: Setup production node_modules
FROM base as production-deps
WORKDIR /myapp
COPY --from=deps /myapp/node_modules /myapp/node_modules
ADD package.json ./
RUN npm prune --omit=dev

# Stage 3: Build the app
FROM base as build
WORKDIR /myapp
COPY --from=deps /myapp/node_modules /myapp/node_modules
ADD . .
RUN npm run build

# Stage 4: Build the production image
FROM base
WORKDIR /myapp
COPY --from=production-deps /myapp/node_modules /myapp/node_modules
COPY --from=build /myapp/build /myapp/build
COPY --from=build /myapp/public /myapp/public
ADD . .

CMD ["npm", "start"]
```

This builds our Remix app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
.cache
build
public/build
```

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](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Solid (`www/src/content/docs/docs/start/aws/solid.mdx`)

---
title: SolidStart on AWS with SST
description: Create and deploy a SolidStart app to AWS with SST.
---

There are two ways to deploy SolidStart apps to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#containers)

We'll use both to build a couple of simple apps below.

---

#### Examples

We also have a few other SolidStart examples that you can refer to.

- [Adding a WebSocket endpoint to SolidStart](/docs/examples/#aws-solidstart-websocket-endpoint)

---

## Serverless

We are going to create a SolidStart app, add an S3 Bucket for file uploads, and deploy it using the `SolidStart` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-solid) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npm init solid@latest aws-solid-start
cd aws-solid-start
```

We are picking the **SolidStart**, **_basic_**, and **_TypeScript_** 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 `app.config.ts` with something like this.

```diff lang="ts" title="app.config.ts"
export default defineConfig({
+  server: {
+    preset: "aws-lambda",
+    awsLambda: {
+      streaming: true,
+    },
+  },
});
```

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your SolidStart app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your SolidStart 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 `SolidStart` component.

##### Link the bucket

Now, link the bucket to our SolidStart app.

```js title="sst.config.ts" {2}
new sst.aws.SolidStart("MyWeb", {
  link: [bucket],
});
```

---

### 3. Generate a pre-signed URL

When our app loads, we'll generate a pre-signed URL for the file upload and use it in our form. Add this below the imports in `src/routes/index.tsx`.

```ts title="src/routes/index.tsx" {5}
async function presignedUrl() {
  "use server";
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });
  return await getSignedUrl(new S3Client({}), command);
}

export const route = {
  load: () => presignedUrl(),
};
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the relevant imports.

```tsx title="src/app.tsx"
import { Resource } from "sst";
import { createAsync } from "@solidjs/router";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
```

And install the npm packages.

```bash
npm install @solidjs/router @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

---

### 4. Create an upload form

Add a form to upload files to the presigned URL. Replace the `Home` component in `src/routes/index.tsx` with:

```tsx title="src/routes/index.tsx"
export default function Home() {
  const url = createAsync(() => presignedUrl());

  return (
    <main>
      <h1>Hello world!</h1>
      <form
        onSubmit={async (e) => {
          e.preventDefault();

          const file = (e.target as HTMLFormElement).file.files?.[0]!;

          const image = await fetch(url() as string, {
            body: file,
            method: "PUT",
            headers: {
              "Content-Type": file.type,
              "Content-Disposition": `attachment; filename="${file.name}"`,
            },
          });

          window.location.href = image.url.split("?")[0];
        }}
      >
        <input name="file" type="file" accept="image/png, image/jpeg" />
        <button type="submit">Upload</button>
      </form>
    </main>
  );
}
```

Head over to the local app in your browser, `http://localhost:3000` 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.

Congrats! Your site should now be live!



---

## Containers

We are going to build a hit counter SolidStart app with Redis. We'll deploy it to AWS in a container using the `Cluster` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-solid-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npm init solid@latest aws-solid-container
cd aws-solid-container
```

We are picking the **SolidStart**, **_basic_**, and **_TypeScript_** 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 `app.config.ts`. Instead we'll use the **default Node preset**.

```ts title="app.config.ts"
import { defineConfig } from "@solidjs/start/config";

export default defineConfig({});
```

---

### 2. Add a Cluster

To deploy our SolidStart app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in your `sst.config.ts`.

```js title="sst.config.ts" {10-12}
async run() {
  const vpc = new sst.aws.Vpc("MyVpc", { bastion: true });
  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",
    },
  });
}
```

This creates a VPC with a bastion host, an ECS Cluster, and adds a Fargate service to it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our SolidStart app locally in dev mode.

---

### 3. Add Redis

Let's add an [Amazon ElastiCache](https://aws.amazon.com/elasticache/) Redis cluster. Add this below the `Vpc` component in your `sst.config.ts`.

```js title="sst.config.ts"
const redis = new sst.aws.Redis("MyRedis", { vpc });
```

This shares the same VPC as our ECS cluster.

---

#### Link Redis

Now, link the Redis cluster to the container.

```ts title="sst.config.ts" {3}
new sst.aws.Service("MyService", {
  // ...
  link: [redis],
});
```

This will allow us to reference the Redis cluster in our SolidStart app.

---

#### Install a tunnel

Since our Redis cluster is in a VPC, we'll need a tunnel to connect to it from our local machine.

```bash "sudo"
sudo npx sst tunnel install
```

This needs _sudo_ to create a network interface on your machine. You'll only need to do this once on your machine.

---

#### Start dev mode

Start your app in dev mode.

```bash
npx sst dev
```

This will deploy your app, start a tunnel in the **Tunnel** tab, and run your SolidStart app locally in the **MyServiceDev** tab.

---

### 4. Connect to Redis

We want the `/` route to increment a counter in our Redis cluster. Let's start by installing the packages we'll use.

```bash
npm install ioredis @solidjs/router
```

We'll increment the counter when the route loads. Replace your `src/routes/index.tsx` with:

```ts title="src/routes/index.tsx" {8}
import { Resource } from "sst";
import { Cluster } from "ioredis";
import { createAsync, cache } from "@solidjs/router";

const getCounter = cache(async () => {
  "use server";
  const redis = new Cluster(
    [{ host: Resource.MyRedis.host, port: Resource.MyRedis.port }],
    {
      dnsLookup: (address, callback) => callback(null, address),
      redisOptions: {
        tls: {},
        username: Resource.MyRedis.username,
        password: Resource.MyRedis.password,
      },
    }
  );

  return await redis.incr("counter");
}, "counter");

export const route = {
  load: () => getCounter(),
};
```

:::tip
We are directly accessing our Redis cluster with `Resource.MyRedis.*`.
:::

Let's update our component to show the counter. Add this to your `src/routes/index.tsx`.

```tsx title="src/routes/index.tsx"
export default function Page() {
  const counter = createAsync(() => getCounter());

  return <h1>Hit counter: {counter()}</h1>;
}
```

---

#### Test your app

Let's head over to `http://localhost:3000` in your browser and it'll show the current hit counter.

You should see it increment every time you **refresh the page**.

---

### 5. Deploy your app

To deploy our app we'll add a `Dockerfile`.

<details>
<summary>View Dockerfile</summary>

```dockerfile title="Dockerfile"
FROM node:lts AS base

WORKDIR /src

# Build
FROM base as build

COPY --link package.json package-lock.json ./
RUN npm install

COPY --link . .

RUN npm run build

# Run
FROM base

ENV PORT=3000
ENV NODE_ENV=production

COPY --from=build /src/.output /src/.output

CMD [ "node", ".output/server/index.mjs" ]
```

</details>

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
```

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](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Svelte (`www/src/content/docs/docs/start/aws/svelte.mdx`)

---
title: SvelteKit on AWS with SST
description: Create and deploy a SvelteKit app to AWS with SST.
---

There are two ways to deploy a SvelteKit app to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#containers)

We'll use both to build a couple of simple apps below.

---

#### Examples

We also have a few other SvelteKit examples that you can refer to.

- [Hit counter with Redis and SvelteKit in a container](/docs/examples/#aws-sveltekit-container-with-redis)

---

## Serverless

We are going to create a SvelteKit app, add an S3 Bucket for file uploads, and deploy it using the `SvelteKit` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-svelte-kit) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npx sv create aws-svelte-kit
cd aws-svelte-kit
```

We are picking the **_SvelteKit minimal_** and **_Yes, using TypeScript syntax_** 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 `svelte.config.mjs` with something like this.

```diff lang="js" title="svelte.config.mjs"
- import adapter from '@sveltejs/adapter-auto';
+ import adapter from "svelte-kit-sst";
```

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your SvelteKit app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your SvelteKit 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 `SvelteKit` component.

##### Link the bucket

Now, link the bucket to our SvelteKit app.

```js title="sst.config.ts" {2}
new sst.aws.SvelteKit("MyWeb", {
  link: [bucket]
});
```

---

### 3. Create an upload form

Let's add a file upload form. Replace your `src/routes/+page.svelte`. This will upload a file to a given pre-signed upload URL.

```svelte title="src/routes/+page.svelte"
<script>
  /** @type {import('./$types').PageData} */
  export let data;

  const handleSubmit = async (e) => {
    const formData = new FormData(e.target);
    const file = formData.get("file");

    const image = await fetch(data.url, {
      body: file,
      method: "PUT",
      headers: {
        "Content-Type": file.type,
        "Content-Disposition": `attachment; filename="${file.name}"`,
      },
    });

    window.location.href = image.url.split("?")[0];
  };
</script>

<section>
  <form on:submit|preventDefault={handleSubmit}>
    <input name="file" type="file" accept="image/png, image/jpeg" />
    <button type="submit">Upload</button>
  </form>
</section>
```

Add some styles.

```css title="src/routes/+page.svelte"
<style>
  section {
    flex: 0.6;
    display: flex;
    padding-top: 4rem;
    align-items: center;
    flex-direction: column;
    justify-content: center;
  }
</style>
```

---

### 4. Generate a pre-signed URL

When our route loads, we'll generate a pre-signed URL for S3 and our form will upload to it. Create a new `src/routes/+page.server.ts` and add the following.

```ts title="src/routes/+page.server.ts" {5}
/** @type {import('./$types').PageServerLoad} */
export async function load() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    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`.
:::

Add the relevant imports.

```ts title="src/routes/+page.server.ts"
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Head over to the local SvelteKit app 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.

Congrats! Your app should now be live!

---

## Containers

We are going to create a SvelteKit app, 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](https://github.com/sst/sst/tree/dev/examples/aws-svelte-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our project.

```bash
npx sv create aws-svelte-container
cd aws-svelte-container
```

We are picking the **_SvelteKit minimal_** and **_Yes, using TypeScript syntax_** 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 `svelte.config.mjs`. But **we'll instead use** the [Node.js adapter](https://kit.svelte.dev/docs/adapter-node) since we're deploying it through a container.

```bash
npm i -D @sveltejs/adapter-node
```

And updating your `svelte.config.js`.

```diff lang="js" title="svelte.config.mjs"
- import adapter from '@sveltejs/adapter-auto';
+ import adapter from '@sveltejs/adapter-node';
```

---

### 2. Add a Service

To deploy our SvelteKit app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in your `sst.config.ts`.

```js 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: "3000/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 SvelteKit app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your SvelteKit app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your SvelteKit app 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.

---

##### Link the bucket

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 SvelteKit app.

---

### 4. Create an upload form

Let's add a file upload form. Replace your `src/routes/+page.svelte`. This will upload a file to a given pre-signed upload URL.

```svelte title="src/routes/+page.svelte"
<script>
  /** @type {import('./$types').PageData} */
  export let data;

  const handleSubmit = async (e) => {
    const formData = new FormData(e.target);
    const file = formData.get("file");

    const image = await fetch(data.url, {
      body: file,
      method: "PUT",
      headers: {
        "Content-Type": file.type,
        "Content-Disposition": `attachment; filename="${file.name}"`,
      },
    });

    window.location.href = image.url.split("?")[0];
  };
</script>

<section>
  <form on:submit|preventDefault={handleSubmit}>
    <input name="file" type="file" accept="image/png, image/jpeg" />
    <button type="submit">Upload</button>
  </form>
</section>
```

Add some styles.

```css title="src/routes/+page.svelte"
<style>
  section {
    flex: 0.6;
    display: flex;
    padding-top: 4rem;
    align-items: center;
    flex-direction: column;
    justify-content: center;
  }
</style>
```

---

### 5. Generate a pre-signed URL

When our route loads, we'll generate a pre-signed URL for S3 and our form will upload to it. Create a new `src/routes/+page.server.ts` and add the following.

```ts title="src/routes/+page.server.ts" {5}
/** @type {import('./$types').PageServerLoad} */
export async function load() {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    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`.
:::

Add the relevant imports.

```ts title="src/routes/+page.server.ts"
import { Resource } from "sst";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";

```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Head over to the local SvelteKit app in your browser, `http://localhost:5173` 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`.

```diff lang="dockerfile" title="Dockerfile"
FROM node:18.18.0-alpine AS builder

WORKDIR /app
COPY package*.json .
RUN npm install
COPY . .
RUN npm run build
RUN npm prune --prod

FROM builder AS deployer

WORKDIR /app
COPY --from=builder /app/build build/
COPY --from=builder /app/package.json .
EXPOSE 3000
ENV NODE_ENV=production
CMD [ "node", "build" ]
```

This builds our SvelteKit app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
.DS_Store
node_modules
```

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](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Tanstack (`www/src/content/docs/docs/start/aws/tanstack.mdx`)

---
title: TanStack Start on AWS with SST
description: Create and deploy a TanStack Start app to AWS with SST.
---

We are going to create a TanStack Start app, add an S3 Bucket for file uploads, and deploy it using the `TanStackStart` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-tanstack-start) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npm create @tanstack/start@latest
```

Now use the init wizard to setup your TanStack Start project:
- Name: aws-start-basic
- Tailwind: `<your choice>`
- Toolchain: `ESLint`
- Deployment Adapter: `Nitro (agnostic)`
- Add-ons: `<your choice>`
- Examples: none

This will create a project with the basic options, and Nitro pre-installed for deployments.

---

##### 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.

##### Configure Nitro

We use Nitro to serve our Tanstack Start app on AWS. You will need to add the correct nitro settings to the `vite.config.ts` file.

```ts title="vite.config.ts"
import { defineConfig } from 'vite'
import { devtools } from '@tanstack/devtools-vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
import viteReact from '@vitejs/plugin-react'
import viteTsConfigPaths from 'vite-tsconfig-paths'
import tailwindcss from '@tailwindcss/vite'
import { nitro } from 'nitro/vite'

const config = defineConfig({
  plugins: [
    devtools(),
    nitro(),
    // this is the plugin that enables path aliases
    viteTsConfigPaths({
      projects: ['./tsconfig.json'],
    }),
    tailwindcss(),
    tanstackStart(),
    viteReact(),
  ],
  nitro: {
    preset: 'aws-lambda',
    awsLambda: {
      streaming: true
    }
  }
})

export default config
```

---

##### Start dev mode

Run the following to start dev mode. This'll start SST and your TanStack Start app.

```bash
npx sst dev
```

Once complete, click on **MyWeb** in the sidebar and open your TanStack Start 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`.

```ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket", {
  access: "public"
});
```

Add this above the `TanStackStart` component.

##### Link the bucket

Now, link the bucket to our TanStack Start app.

```js title="sst.config.ts" {2}
new sst.aws.TanStackStart("MyWeb", {
  link: [bucket]
});
```

---

### 3. Create an upload form

Add a form component in `src/components/Form.tsx`.

```tsx title="src/components/Form.tsx"
import './Form.css'

export default function Form({ url }: { url: string }) {
  return (
    <form
      className='form'
      onSubmit={async (e) => {
        e.preventDefault()

        const file = (e.target as HTMLFormElement).file.files?.[0] ?? null

        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]
      }}
    >
      <input name='file' type='file' accept='image/png, image/jpeg' />
      <button type='submit'>Upload</button>
    </form>
  )
}
```

Add some styles.

```css title="src/components/Form.css"
.form {
  padding: 2rem;
}

.form input {
  margin-right: 1rem;
}

.form button {
  appearance: none;
  padding: 0.5rem 0.75rem;
  font-weight: 500;
  font-size: 0.875rem;
  border-radius: 0.375rem;
  border: 1px solid rgba(68, 107, 158, 0);
  background-color: rgba(68, 107, 158, 0.1);
}

.form button:active:enabled {
  background-color: rgba(68, 107, 158, 0.2);
}
```

---

### 4. Generate a pre-signed URL

When our route loads, we'll generate a pre-signed URL for S3 and our form will upload to it. Add a server function and a route loader in `src/routes/index.tsx`.

```ts title="src/routes/index.tsx" {4}
export const getPresignedUrl = createServerFn().handler(async () => {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name
  })
  return await getSignedUrl(new S3Client({}), command)
})

export const Route = createFileRoute('/')({
  component: RouteComponent,
  loader: async () => {
    return { url: await getPresignedUrl() }
  }
})

function RouteComponent() {
  const { url } = Route.useLoaderData()
  return (
    <main>
      <Form url={url} />
    </main>
  )
}
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Add the relevant imports.

```ts title="src/routes/+page.server.ts"
import { Resource } from 'sst'
import Form from '~/components/Form'
import { createServerFn } from '@tanstack/react-start'
import { createFileRoute } from '@tanstack/react-router'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Head over to the local TanStack Start app in your browser, `http://localhost:3000` 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.

Congrats! Your app should now be live!

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and view logs from it.



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Aws Trpc (`www/src/content/docs/docs/start/aws/trpc.mdx`)

---
title: tRPC on AWS with SST
description: Create and deploy a tRPC API in AWS with SST.
---

We are going to build a serverless [tRPC](https://trpc.io) API, a simple client, and deploy it to AWS using SST.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-trpc) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

## 1. Create a project

Let's start by creating our app.

```bash
mkdir my-trpc-app && cd my-trpc-app
npm init -y
```

#### 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.

---

## 2. Add the API

Let's add two Lambda functions; one for our tRPC server and one that'll be our client. Update your `sst.config.ts`.

```js title="sst.config.ts" {9}
async run() {
  const trpc = new sst.aws.Function("Trpc", {
    url: true,
    handler: "index.handler",
  });

  const client = new sst.aws.Function("Client", {
    url: true,
    link: [trpc],
    handler: "client.handler",
  });

  return {
    api: trpc.url,
    client: client.url,
  };
}
```

We are linking the server to our client. This will allow us to access the URL of the server in our client.

---

#### Start dev mode

Start your app in dev mode. This runs your functions [_Live_](/docs/live/).

```bash
npx sst dev
```

This will give you two URLs.

```bash frame="none"
+  Complete
   api: https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws
   client: https://3x4y4kg5zv77jeroxsrnjzde3q0tgxib.lambda-url.us-east-1.on.aws
```

---

## 3. Create the server

Let's create our tRPC server. Add the following to `index.ts`.

```ts title="index.ts"
const t = initTRPC
  .context<CreateAWSLambdaContextOptions<APIGatewayProxyEvent | APIGatewayProxyEventV2>>()
  .create();

const router = t.router({
  greet: t.procedure
    .input(z.object({ name: z.string() }))
    .query(({ input }) => {
      return `Hello ${input.name}!`;
    }),
});

export type Router = typeof router;

export const handler = awsLambdaRequestHandler({
  router: router,
  createContext: (opts) => opts,
});
```

We are creating a simple method called `greet` that takes a _string_ as an input.

Add the imports.

```ts title="index.ts"
import { z } from "zod";
import {
  awsLambdaRequestHandler,
  CreateAWSLambdaContextOptions
} from "@trpc/server/adapters/aws-lambda";
import { initTRPC } from "@trpc/server";
import { APIGatewayProxyEvent, APIGatewayProxyEventV2 } from "aws-lambda";
```

And install the npm packages.

```bash
npm install zod @trpc/server@next
```

---

## 4. Add the client

Now we'll connect to our server in our client. Add the following to `client.ts`.

```ts title="client.ts" {4}
const client = createTRPCClient<Router>({
  links: [
    httpBatchLink({
      url: Resource.Trpc.url,
    }),
  ],
});

export async function handler() {
  return {
    statusCode: 200,
    body: await client.greet.query({ name: "Patrick Star" }),
  };
}
```

:::tip
We are accessing our server with `Resource.Trpc.url`.
:::

Add the relevant imports. Notice we are importing the _types_ for our API.

```ts title="index.ts" {2}
import { Resource } from "sst";
import type { Router } from "./index";
import { createTRPCClient, httpBatchLink } from "@trpc/client";
```

Install the client npm package.

```bash
npm install @trpc/client@next
```

---

#### Test your app

To test our app, hit the client URL.

```bash
curl https://3x4y4kg5zv77jeroxsrnjzde3q0tgxib.lambda-url.us-east-1.on.aws
```

This will print out `Hello Patrick Star!`.

---

## 5. Deploy your app

Now let's deploy your app.

```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](/docs/console/) to _**git push to deploy**_ your app and monitor it for any issues. 



You can [create a free account](https://console.sst.dev) and connect it to your AWS account.

---

### Www Src Content Docs Docs Start Cloudflare Hono (`www/src/content/docs/docs/start/cloudflare/hono.mdx`)

---
title: Hono on Cloudflare with SST
description: Create and deploy a Hono API on Cloudflare with SST.
---

We are going to build an API with Hono, add an R2 bucket for file uploads, and deploy it using Cloudflare with SST.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/cloudflare-hono) of this example in our repo.
:::

Before you get started, [create your Cloudflare API token](/docs/cloudflare/#credentials).

---

## 1. Create a project

Let's start by creating our app.

```bash
mkdir my-hono-api && cd my-hono-api
npm init -y
```

---

#### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

Select the defaults and pick **Cloudflare**. This'll create a `sst.config.ts` file in your project root.

---

#### Set the Cloudflare credentials

Set the token and account ID in your shell or `.env` file.

```bash
export CLOUDFLARE_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
export CLOUDFLARE_DEFAULT_ACCOUNT_ID=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
```

---

## 2. Add a Worker

Let's add a Worker. Update your `sst.config.ts`.

```js title="sst.config.ts"
async run() {
  const hono = new sst.cloudflare.Worker("Hono", {
    url: true,
    handler: "index.ts",
  });

  return {
    api: hono.url,
  };
}
```

We are enabling the Worker URL, so we can use it as our API.

---

## 3. Add an R2 Bucket

Let's add an R2 bucket for file uploads. Update your `sst.config.ts`.

```ts title="sst.config.ts"
const bucket = new sst.cloudflare.Bucket("MyBucket");
```

Add this above the `Worker` component.

#### Link the bucket

Now, link the bucket to our Worker.

```ts title="sst.config.ts" {3}
const hono = new sst.cloudflare.Worker("Hono", {
  url: true,
  link: [bucket],
  handler: "index.ts",
});
```

---

## 4. Upload a file

We want the `/` route of our API to upload a file to the R2 bucket. Create an `index.ts` file and add the following.

```ts title="index.ts" {4-8}
const app = new Hono()
  .put("/*", async (c) => {
    const key = crypto.randomUUID();
    await Resource.MyBucket.put(key, c.req.raw.body, {
      httpMetadata: {
        contentType: c.req.header("content-type"),
      },
    });
    return c.text(`Object created with key: ${key}`);
  });

export default app;
```

:::tip
We are uploading to our R2 bucket with the SDK — `Resource.MyBucket.put()`
:::

Add the imports.

```ts title="index.ts"
import { Hono } from "hono";
import { Resource } from "sst";
```

And install the npm packages.

```bash
npm install hono
```

---

## 5. Download a file

We want to download the last uploaded file if you make a `GET` request to the API. Add this to your routes in `index.ts`.

```ts title="index.ts"
const app = new Hono()
  // ...
  .get("/", async (c) => {
    const first = await Resource.MyBucket.list().then(
      (res) =>
        res.objects.sort(
          (a, b) => a.uploaded.getTime() - b.uploaded.getTime(),
        )[0],
    );
    const result = await Resource.MyBucket.get(first.key);
    c.header("content-type", result.httpMetadata.contentType);
    return c.body(result.body);
  });
```

We are getting a list of the files in the files in the bucket with `Resource.MyBucket.list()` and we are getting a file for the given key with `Resource.MyBucket.get()`.

---

#### Start dev mode

Start your app in dev mode.

```bash
npx sst dev
```

This will give you the URL of your API.

```bash frame="none"
✓  Complete
   Hono: https://my-hono-api-jayair-honoscript.sst-15d.workers.dev
```

---

#### Test your app

Let's try uploading a file from your project root. Make sure to use your API URL.

```bash
curl -X PUT --upload-file package.json https://my-hono-api-jayair-honoscript.sst-15d.workers.dev
```

Now head over to `https://my-hono-api-jayair-honoscript.sst-15d.workers.dev` in your browser and it'll load the file you just uploaded.

---

## 6. Deploy your app

Finally, let's deploy your app!

```bash
npx sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production.

---

### Www Src Content Docs Docs Start Cloudflare Trpc (`www/src/content/docs/docs/start/cloudflare/trpc.mdx`)

---
title: tRPC on Cloudflare with SST
description: Create and deploy a tRPC API in Cloudflare with SST.
---

We are going to build a [tRPC](https://trpc.io) API, a simple client, and deploy it to Cloudflare using SST.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/cloudflare-trpc) of this example in our repo.
:::

Before you get started, [create your Cloudflare API token](/docs/cloudflare/#credentials).

---

## 1. Create a project

Let's start by creating our app.

```bash
mkdir my-trpc-app && cd my-trpc-app
npm init -y
```

---

#### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

Select the defaults and pick **Cloudflare**. This'll create a `sst.config.ts` file in your project root.

---

#### Set the Cloudflare credentials

You can save your Cloudflare API token in a `.env` file or just set it directly.

```bash
export CLOUDFLARE_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
export CLOUDFLARE_DEFAULT_ACCOUNT_ID=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
```

---

## 2. Add the API

Let's add two Workers; one for our tRPC server and one that'll be our client. Update your `sst.config.ts`.

```js title="sst.config.ts" {9}
async run() {
  const trpc = new sst.cloudflare.Worker("Trpc", {
    url: true,
    handler: "index.ts",
  });

  const client = new sst.cloudflare.Worker("Client", {
    url: true,
    link: [trpc],
    handler: "client.ts",
  });

  return {
    api: trpc.url,
    client: client.url,
  };
}
```

We are linking the server to our client. This will allow us to access the server in our client.

---

## 3. Create the server

Let's create our tRPC server. Add the following to `index.ts`.

```ts title="index.ts"
const t = initTRPC.context().create();

const router = t.router({
  greet: t.procedure
    .input(z.object({ name: z.string() }))
    .query(({ input }) => {
      return `Hello ${input.name}!`;
    }),
});

export type Router = typeof router;

export default {
  async fetch(request: Request): Promise<Response> {
    return fetchRequestHandler({
      router,
      req: request,
      endpoint: "/",
      createContext: (opts) => opts,
    });
  },
};
```

We are creating a simple method called `greet` that takes a _string_ as an input.

Add the relevant imports.

```ts title="index.ts"
import { z } from "zod";
import { initTRPC } from "@trpc/server";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
```

And install the npm packages.

```bash
npm install zod @trpc/server@next
```

---

## 4. Add the client

Now we'll connect to our server in our client. Add the following to `client.ts`.

```ts title="client.ts" {8}
export default {
  async fetch() {
    const client = createTRPCClient<Router>({
      links: [
        httpBatchLink({
          url: "http://localhost/",
          fetch(req) {
            return Resource.Trpc.fetch(req);
          },
        }),
      ],
    });
    return new Response(
      await client.greet.query({
        name: "Patrick Star",
      }),
    );
  },
};
```

:::tip
We are accessing our server with `Resource.Trpc.fetch()`.
:::

Add the imports. Notice we are importing the _types_ for our API.

```ts title="index.ts" {2}
import { Resource } from "sst";
import type { Router } from "./index";
import { createTRPCClient, httpBatchLink } from "@trpc/client";
```

Install the client npm package.

```bash
npm install @trpc/client@next
```

---

#### Start dev mode

Start your app in dev mode.

```bash
npx sst dev
```

This will give you two URLs.

```bash frame="none"
+  Complete
   api: https://my-trpc-app-jayair-trpcscript.sst-15d.workers.dev
   client: https://my-trpc-app-jayair-clientscript.sst-15d.workers.dev
```

---

#### Test your app

To test our app, hit the client URL.

```bash
curl https://my-trpc-app-jayair-clientscript.sst-15d.workers.dev
```

This will print out `Hello Patrick Star!`.

---

## 5. Deploy your app

Now let's deploy your app.

```bash
npx sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production.

---

### Www Src Content Docs Docs Start Cloudflare Worker (`www/src/content/docs/docs/start/cloudflare/worker.mdx`)

---
title: Cloudflare Workers with SST
description: Create and deploy a Cloudflare Worker as an API with SST.
---

We are going to build an API with a Cloudflare Worker, add an R2 bucket for file uploads, and deploy it using SST.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/cloudflare-worker) of this example in our repo.
:::

Before you get started, [create your Cloudflare API token](/docs/cloudflare/#credentials).

---

## 1. Create a project

Let's start by creating our app.

```bash
mkdir my-worker && cd my-worker
npm init -y
```

---

#### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

Select the defaults and pick **Cloudflare**. This'll create a `sst.config.ts` file in your project root.

---

#### Set the Cloudflare credentials

Set the token and account ID in your shell or `.env` file.

```bash
export CLOUDFLARE_API_TOKEN=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
export CLOUDFLARE_DEFAULT_ACCOUNT_ID=aaaaaaaa_aaaaaaaaaaaa_aaaaaaaa
```

---

## 2. Add a Worker

Let's add a Worker. Update your `sst.config.ts`.

```js title="sst.config.ts"
async run() {
  const worker = new sst.cloudflare.Worker("MyWorker", {
    handler: "./index.ts",
    url: true,
  });

  return {
    api: worker.url,
  };
}
```

We are enabling the Worker URL, so we can use it as our API.

---

## 3. Add an R2 Bucket

Let's add an R2 bucket for file uploads. Update your `sst.config.ts`.

```js title="sst.config.ts"
const bucket = new sst.cloudflare.Bucket("MyBucket");
```

Add this above the `Worker` component.

#### Link the bucket

Now, link the bucket to our Worker.

```ts title="sst.config.ts" {3}
const worker = new sst.cloudflare.Worker("MyWorker", {
  handler: "./index.ts",
  link: [bucket],
  url: true,
});
```

---

## 4. Upload a file

We want our API to upload a file to the R2 bucket if you make a `PUT` request to it. Create an `index.ts` file and add the following.

```ts title="index.ts" {5-9}
export default {
  async fetch(req: Request) {
    if (req.method == "PUT") {
      const key = crypto.randomUUID();
      await Resource.MyBucket.put(key, req.body, {
        httpMetadata: {
          contentType: req.headers.get("content-type"),
        },
      });
      return new Response(`Object created with key: ${key}`);
    }
  },
};
```

:::tip
We are uploading to our R2 bucket with the SDK — `Resource.MyBucket.put()`
:::

Import the SDK.

```ts title="index.ts"
import { Resource } from "sst";
```

---

## 5. Download a file

We want to download the last uploaded file if you make a `GET` request to the API. Add this to the `fetch` function in your `index.ts` file.

```ts title="index.ts"
if (req.method == "GET") {
  const first = await Resource.MyBucket.list().then(
    (res) =>
      res.objects.toSorted(
        (a, b) => a.uploaded.getTime() - b.uploaded.getTime(),
      )[0],
  );
  const result = await Resource.MyBucket.get(first.key);
  return new Response(result.body, {
    headers: {
      "content-type": result.httpMetadata.contentType,
    },
  });
}
```

We are getting a list of the files in the files in the bucket with `Resource.MyBucket.list()` and we are getting a file for the given key with `Resource.MyBucket.get()`.

---

#### Start dev mode

Start your app in dev mode.

```bash
npx sst dev
```

This will give you the URL of your API.

```bash frame="none"
+  Complete
   api: https://start-cloudflare-jayair-myworkerscript.sst-15d.workers.dev
```

---

#### Test your app

Let's try uploading a file from your project root. Make sure to use your API URL.

```bash
curl --upload-file package.json https://start-cloudflare-jayair-myworkerscript.sst-15d.workers.dev
```

Now head over to `https://start-cloudflare-jayair-myworkerscript.sst-15d.workers.dev` in your browser and it'll load the file you just uploaded.

---

## 6. Deploy your app

Finally, let's deploy your app!

```bash
npx sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production.

---

--- METRICS ---
- Files Extracted: 97
- Estimated Token Budget: ~160887 tokens
- Canonical Reference: https://codewiki.google/github.com/anomalyco/sst
