Migrate From v2
Migrate your SST v2 apps to v3.
This guide will help you migrate your SST v2 apps to v3. We look at the major differences between v2 and v3 below. But to get a quick intro, we recommend reading the What is SST and Workflow docs.
We’ll then go over a migration plan that you can use. The exact details of this will be different from team to team depending on the resources in it, and sensitivity of downtime.
Getting help
SST v3 has been around for a few months with a pretty sizeable community on Discord. We’ve created a channel for folks looking to migrate.
Join #migrate-from-v2
on Discord.
Not supported
While the goal with v3 is to support most of what’s in v2, there are a couple of things that haven’t been supported yet. If you rely on these heavily in your app, we recommend waiting a bit longer till we support them.
Construct | GitHub Issue |
---|---|
Job | #170 |
Auth | #90 |
Script | #811 |
EventBus | #67 |
RDS MySQL | #812 |
Function non-Node.js runtimes | Python, Container, Custom, Go |
Feel free to let us know via the linked GitHub issues if these are blockers for you. It’ll help us prioritize this list.
Major changes
If you are coming from SST v2, it’s worth starting with the big differences between v2 and v3. It’ll help you understand the types of changes you’ll need to make as you migrate.
No CloudFormation
Let’s start with the obvious. SST v3 moves away from CloudFormation and CDK, we’ve written in detail about why we decided to do this.
No CloudFormation, means a couple of things:
- There are no stacks, all the resources are defined through the same function in the
sst.config.ts
. - 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.
- The state of your app is stored locally and backed up to S3. Learn more about State.
No CDK
And moving away from CDK means:
-
You cannot fall back to CDK constructs if something isn’t supported by SST. Instead there is the AWS Classic provider from Pulumi that’s built on Terraform. There are also 150+ other providers that allow you to build on any cloud. Check out the Directory.
If you are using a lot of higher level CDK constructs in your v2 app, it’s going to be really hard to migrate to v3. The Pulumi/Terraform ecosystem is fairly complete but it’s mainly made up of low level resources. You might not have ready replacements for your CDK constructs.
-
Since the constructs or components are no longer built on CDK; they don’t have a
cdk
prop. Instead, there’s atransform
prop that lets you modify the props that a component sends to its underlying resources. Learn more about thetransform
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.
Learn more about the new sst.config.ts
.
sst dev
The sst dev
CLI has been completely reworked. It now runs a multiplexer that deploys your app and runs your frontends together. So you don’t need to:
- Start your frontend separately
- Need to wrap your frontend
dev
script withsst bind
Learn more about sst dev
.
sst build
There is no sst build
CLI. Instead you can run sst diff
to see what changes will be deployed, without doing an actual deploy.
Learn more about sst diff
.
Resource binding
Resource binding is now called resource linking, the bind
prop is now renamed to link
. The Node.js client or JS SDK has been reworked so that all linked resources are now available through the Resource
object. We’ll look at this in detail below.
The client handlers and hooks have not been supported yet.
Learn more about Resource linking.
Secrets
Secrets are not stored in SSM. Instead they are encrypted and stored in your state file. It’s encrypted using a passphrase that’s stored in SSM.
Loading secrets in your functions no longer needs a top-level await.
Migration plan
Say you have a v2 app in a git repo that’s currently deployed to production. Here’s how we recommend carrying out the migration.
- 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.
- Test your non-prod version of your v3 app.
- Then for your prod stage, follow the steps below and make the import, domain, and subscriber changes.
- Once the prod version of your v3 app is running, clean up some of the v2 prod resources.
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
-
Start by setting the removal policy to
retain
in the v2 app for the production stages. This ensures resources don’t get accidentally removed. -
Create a new branch in your repo for the upcoming changes.
-
For the prod version of the v3 app, pick a different stage name. Say your prod stage in v2 is called
production
. Maybe useprod
,main
, orlive
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.
-
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.Ensure v3 is installed.
-
Backup the v2 config with.
-
Init a v3 app.
-
Set the removal policy to
retain
. Similar tosetDefaultRemovalPolicy
in v2, you can configure the removal policy insst.config.ts
in v3.By default, v3 has removal policy set to
retain
for theproduction
stage, andremove
for other stages. -
Deploy an empty app to ensure the app is configured correctly.
-
Update the dev scripts for your frontend. Remove the
sst bind
from thedev
script in yourpackage.json
. For example, for a Next.js app. -
Remove any CDK related packages from your
package.json
.
Migrate stacks
Now before we start making changes to our constructs, you might have some stacks code in your sst.config.ts
.
Take a look at the list below and apply the changes that matter to you.
Restructure
Since you don’t have to import the constructs and there are no stacks, you’ll need to change how your constructs are structured.
For example, in the monorepo notes app we made these changes.
We store our infrastructure files in the infra/
directory in v3. You can refer to the demo notes app to see how these are structured.
Migrate runtime
For your runtime code, your functions and frontend; there are fairly minimal changes. The clients or the JS SDK have been reorganized.
You can make these changes now or as you are migrating each construct. Check out the steps below.
Migrate constructs
Constructs in v2 have their equivalent components in v3. Constructs fall into roughly these 3 categories:
- Transient — these don’t contain data, like
Function
,Topic
, orQueue
. - Data — these contain application data, like
RDS
,Table
, orBucket
. - Custom domains — these have custom domains configured, like
Api
,StaticSite
, orNextjsSite
. - Subscribers — these are constructs that subscribe to other constructs, like the
Bucket
,Queue
, orTable
subscribers.
We’ll go over each of these types and copy our v2 constructs over as v3 components.
Transient constructs
Constructs like Function
, Cron
, Topic
, Queue
, and KinesisStream
do not contain data. They can be recreated in the v3 app.
Simply copy them over using the reference below.
Data constructs
Constructs like RDS
, Table
, Bucket
, and Cognito
contain data. If you do not need to keep the data, you can recreate them like what you did above. This is often the case for non-production stages.
However, for production stages, you need to import the underlying AWS resource into the v3 app.
For example, here are the steps for importing an S3 bucket named app-prod-MyBucket
.
-
Import the resource
Say the bucket is defined in SST v2, and the bucket name is
app-prod-MyBucket
.You can use the
import
andtransform
props to import it.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.
-
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.
-
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.
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.
-
First, create the resource in v3 without a custom domain. So for
Nextjs
for example. -
Deploy your v3 app.
-
When you are ready, flip the domain using the
override
prop.This updates the DNS record to point to your new Next.js app.
And deploy again.
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.
- Deploy the v3 app without the subscribers. Either by commenting out the
.subscribe()
call, or by returning early in the subscriber function. - When you are ready to flip, remove the subscribers in the v2 app and deploy.
- 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.
The resources that were recreated in v3, the ones that were not imported, can now be removed. However, since we have v2 app set to retain
, this is going to be a manual process.
You can go to the CloudFormation console, look at the list of resources in your v2 app’s stacks and remove them manually.
Finally, when you run sst remove
for your v2 app, it’ll remove the CloudFormation stacks as well.
CI/CD
You probably have git push to deploy or CI/CD set up for your apps. If you are using GitHub Actions; there shouldn’t be much of a difference between v2 and v3.
If you are using Seed to deploy your v2 app; then you’ll want to migrate to using Autodeploy on the SST Console. We are currently not planning to support v3 on Seed.
There are a couple of key reasons to Autodeploy through the Console:
- The builds are run in your AWS account.
- You can configure your workflow through your
sst.config.ts
. - And you can see which resources were updated as a part of the deploy.
To enable Autodeploy on the Console, you’ll need to:
- Create a new account on the Console — console.sst.dev
- Link your AWS account
- Connect your repo
- Configure your environments
- And git push
Learn more about Console and Autodeploy.
sst.config.ts
Listed below are some of the changes to your sst.config.ts
in general.
No imports
All the constructs or components are available in the global context. So there’s no need to import anything. Your app’s package.json
only needs the sst
package. There are no extra CDK or infrastructure related packages.
Globals
There are a couple of global variables, $app
and $dev
that replace the app
argument that’s passed into the stacks()
method of your sst.config.ts
.
$app.name
gives you the name of app. Used to beapp.name
.$app.stage
gives you the name of stage. Used to beapp.stage
.$dev === true
tells you if you are in dev mode. Used to beapp.mode === "dev
.$dev === false
tells you if it’s being deployed. Used to beapp.mode === "deploy
.- There is no
app.mode === remove
replacement since your components are not evaluated onsst remove
. - 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 useaws.getRegionOutput().name
.
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.
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
.
Learn more about $transform
.
Clients
The Node.js client, now called the JS SDK has a couple of minor changes.
Update sst
to the latest version in your package.json
. If you have a monorepo, make sure to update sst
in all your packages.
Bind
In SST v3, you access all bound or linked resources through the Resource
module.
Say you link a bucket to a function.
In your function you would access it like so.
Config
The same applies to Config
as well. Let’s look at a secret.
And in your function you access it in the same way.
Handlers
In v2, some modules in the Node client had handlers and hooks.
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
.
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.
And then import them via the sstv2
alias.
Constructs
Below shows the v3 component version of a v2 construct.
Api
RDS
For migrations, we recommend using Drizzle Kit. Check out our Drizzle example.
Cron
Table
Topic
Queue
Config
The Config
construct is now broken into a Secret
component and v3 has a separate way to bind any value or parameter.
Secret
There’s also a slight change to the CLI for setting secrets.
Parameter
The Linkable
component lets you bind or link any value.
In your function you’d access this using.
Bucket
Service
Cognito
Function
AstroSite
StaticSite
RemixSite
NextjsSite
AppSyncApi
SvelteKitSite
SolidStartSite
WebSocketApi
KinesisStream
ApiGatewayV1Api
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.