Config
Built-in support for securely managing environment variables and secrets.
tip
Want to learn more about Config
? Check out the launch livestream on YouTube.
Overview
Config
allows you to securely pass the following into your functions.
- Secrets: Sensitive values that cannot be defined in your code. You can use the
sst secrets
CLI to set them. - Parameters: Values from non-SST constructs, ie. CDK constructs or static values.
info
If you want to pass values from SST constructs to your functions, you should bind them using Resource Binding.
And once you've defined your Secrets and Parameters, you can fetch them in your Lambda functions with the sst/node/config
package.
Secrets
Config.Secret
allows you to define, set, and fetch secrets in your app. Let's see how it works.
Quick Start
To see how Secrets work, we are going to create a Secret with Stripe secret key and bind it to a Lambda function.
Follow along by creating a new SST app by running npx create-sst@latest
. Alternatively, you can refer to this example repo that's based on the same template.
To create a new secret, open up
stacks/MyStack.ts
and add aConfig.Secret
construct below the API. You can also create a new stack to define all secrets used in your app.stacks/MyStack.tsconst STRIPE_KEY = new Config.Secret(stack, "STRIPE_KEY");
You'll also need to import
Config
at the top.import { Config } from "sst/constructs";
Note that you are not setting the values for the secret in your code. You shouldn't have sensitive values committed to Git.
Bind the
STRIPE_KEY
to theapi
.stacks/MyStack.tsapi.bind([STRIPE_KEY]);
Then in your terminal, run the
sst secrets
CLI to set a value for the secret.npx sst secrets set STRIPE_KEY sk_test_abc123
Now you can access the Stripe key in your API using the
Config
helper. Changepackages/functions/src/lambda.ts
to:packages/functions/src/lambda.tsimport { APIGatewayProxyHandlerV2 } from "aws-lambda";
import { Config } from "sst/node/config";
export const handler: APIGatewayProxyHandlerV2 = async () => {
return {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: `Here is my Stripe key ${Config.STRIPE_KEY}. Don't share with others!`,
};
};That's it!
sst secrets
We used the sst secrets set
CLI in the above example to set a secret. Here are some of the other commands.
npx sst secrets get STRIPE_KEY
to check the value of a secretnpx sst secrets list
to get the values of all the secretsnpx sst secrets remove STRIPE_KEY
to unset the value of a secret
You can also pass in a stage name to manage the secrets for a specific stage.
npx sst secrets list --stage prod
Read more about the sst secrets
CLI.
How it works
Behind the scenes, secrets are stored as AWS SSM Parameters in your AWS account. When you run:
npx sst secrets set STRIPE_KEY sk_test_abc123
An SSM parameter of the type SecureString
is created with the name /sst/{appName}/{stageName}/Secret/STRIPE_KEY/value
, where {appName}
is the name of your SST app, and {stageName}
is the stage you are configuring for. The parameter value sk_test_abc123
gets encrypted and stored in AWS SSM.
Function handler
And when you pass secrets into a function:
new Function(stack, "MyFunction", {
handler: "lambda.handler",
bind: [STRIPE_KEY],
}
It adds a Lambda environment variables named SST_Secret_value_STRIPE_KEY
to the function. The environment variable has a placeholder value __FETCH_FROM_SSM__
to indicate that the value for STRIPE_KEY
needs to be fetched from SSM at runtime.
Top-level await
At runtime when you import the Config
package in your function.
import { Config } from "sst/node/config";
It performs a top-level await to fetch and decrypt STRIPE_KEY
from SSM. Once fetched, you can reference Config.STRIPE_KEY
directly in your code.
note
Due to the use of top-level await, your functions need to be bundled in the esm
format. If you created your app using create-sst
, the bundle format is likely already set to esm
. Here's how to set the Function bundle format.
Note that the secret values are fetched once when the Lambda container first boots up, and the values are cached for subsequent invocations.
Error handling
If you reference a secret that hasn't been set in the bind
prop for the function, you'll get an error. For example, if you reference something like Config.XYZ
and it hasn't been set; you'll get the following runtime error.
Config.XYZ has not been set for this function.
Typesafety
The Config
object in your Lambda function code is also typesafe and your editor should be able to autocomplete the options.
Updating secrets
Secret values are not refetched on subsequent Lambda function invocations. So if the value of a secret changes while the Lambda container is still warm, it'll hang on to the old value.
To address this, SST forces functions to refetch the secret when its value changes. So when you run:
npx sst secrets set STRIPE_KEY sk_test_abc123
SST looks up all the functions using STRIPE_KEY
. And for each function, SST sets a Lambda environment variable named SST_ADMIN_SECRET_UPDATED_AT
with the value of the current timestamp. This will trigger the Lambda containers to restart. If a container is in the middle of handling an invocation, it will restart after the invocation is complete.
Fallback values
Sometimes you might be creating ephemeral stages or preview environments from pull requests. It can be annoying to manually set the value of a secret for these stages especially because they might all be using the same value.
To make this easier, you can set a fallback value for a secret. And if its value isn't set for a stage, it'll use the fallback value instead.
tip
Set a fallback value for your secret so you don't have to set them for ephemeral stages.
In the above example, it's likely all the dev stages share the same STRIPE_KEY
. So set a fallback value by running:
npx sst secrets set --fallback STRIPE_KEY sk_test_abc123
Similar to the set
command, SST creates an AWS SSM Parameter of the type SecureString
. And the parameter name in this case is /sst/{appName}/.fallback/Secret/STRIPE_KEY/value
.
info
The fallback value can only be inherited by stages deployed in the same AWS account and region.
If a function uses the STRIPE_KEY
secret, but neither the secret value or the fallback value has been set, you'll get a runtime error when you import sst/node/config
.
The following secrets were not found: STRIPE_KEY
Parameters
Config.Parameter
allows you to pass values from non-SST constructs, ie. CDK constructs or static values to your Lambda functions. Let's see it in action.
Quick start
To see how Parameters work, we are going to create a Parameter with the version of your app and bind it to a Lambda function.
Follow along by creating a new SST app by running npx create-sst@latest
. Alternatively, you can refer to this example repo that's based on the same template.
To create a new parameter, open up
stacks/MyStack.ts
and add aConfig.Parameter
construct below the API.stacks/MyStack.tsconst APP_VERSION = new Config.Parameter(stack, "APP_VERSION", {
value: "1.2.0",
});You'll also need to import
Config
at the top.import { Config } from "sst/constructs";
Bind the
APP_VERSION
to theapi
.stacks/MyStack.tsapi.bind([APP_VERSION]);
Now you can access the app verion in your API using the Config helper. Change
packages/functions/src/lambda.ts
to:packages/functions/src/lambda.tsimport { APIGatewayProxyHandlerV2 } from "aws-lambda";
import { Config } from "sst/node/config";
export const handler: APIGatewayProxyHandlerV2 = async () => {
return {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: `App version is ${Config.APP_VERSION}.`,
};
};That's it!
How it works
Behind the scenes, parameters are stored as Lambda environment variables. When you pass a parameter into a function:
new Function(stack, "MyFunction", {
handler: "lambda.handler",
bind: [USER_UPDATED_TOPIC],
}
A Lambda environment variable is added to the function, named SST_Parameter_value_USER_UPDATED_TOPIC
with the value of the topic name.
Function handler
At runtime when you import the Config
package in your function.
import { Config } from "sst/node/config";
It reads the value from process.env.SST_Parameter_value_USER_UPDATED_TOPIC
and assigns it to Config.USER_UPDATED_TOPIC
. You can then reference Config.USER_UPDATED_TOPIC
directly in your code.
Copy to SSM
SST also stores a copy of the parameter value in AWS SSM. For each parameter, an SSM parameter of the type String
is created with the name /sst/{appName}/{stageName}/parameters/USER_UPDATED_TOPIC
, where {appName}
is the name of your SST app, and {stageName}
is the stage. The parameter value is the topic name stored in plain text.
Storing the parameter values in SSM might seem redundant. But it provides a convenient way to fetch all the parameters used in your application. This can make it easy to test your functions. Read more about how SST uses Config
to make testing easier.
Error handling
If you reference a parameter that hasn't been set in the bind
prop, you'll get an error. For example, if you reference something like Config.XYZ
and it hasn't been set; you'll get the following runtime error.
Config.XYZ has not been set for this function.
Typesafety
The Config
object in your Lambda function code is also typesafe and your editor should be able to autocomplete the options.
Cost
Secrets and Parameters are stored in AWS SSM with the Standard Parameter type and Standard Throughput. This makes Config
free to use in your SST apps.
Other languages
The Config
Lambda function package only supports JavaScript and TypeScript. For other languages, SST supports loading environment variables using dotenv.
caution
If you are using JavaScript or TypeScript, it's strongly recommended that you use Config
to manage your secrets and parameters.
The .env
support in SST is similar to what Create React App and Next.js do for environment variables. For example if you add the following .env
file to your project root.
TABLE_READ_CAPACITY=5
TABLE_WRITE_CAPACITY=5
SST will load the process.env.TABLE_READ_CAPACITY
and process.env.TABLE_WRITE_CAPACITY
variables into the Node.js environment; automatically allowing you to use them in your CDK code.
Types of .env
files
Aside from the default .env
file, there are a couple of other types of .env
files. You can use them to better organize the environment variables in your SST app.
.env.{stageName}
You can add a
.env.{stageName}
file to override the default values for a specific stage. For example, this overrides the value for theprod
stage:.env.prodTABLE_READ_CAPACITY=20
.env*.local
You can also add
.env.local
and.env.{stageName}.local
files to set up environment variables that are specific to your local machine.
Here's the priority in which these files are loaded. Starting with the one that has the highest priority.
.env.dev.local
.env.dev
.env.local
.env
Assume that the current stage is dev
.
Committing .env
files
The .env
and .env.{stageName}
files can be committed to Git. On the other hand, the .env.local
and .env.{stageName}.local
shouldn't.
The .env*.local
files are meant to specify sensitive information specific to your machine. They should be ignored through the .gitignore
file.
caution
Don't commit any .env
files to Git that contain sensitive information.
Note that, SST doesn't enforce these conventions. They are just guidelines that you can use to organize your environment variables. Similar to the ones used by Create React App and Next.js.
Expanding variables
SST will also automatically expand variables ($VAR
). For example:
DEFAULT_READ_CAPACITY=5
USERS_TABLE_READ_CAPACITY=$DEFAULT_READ_CAPACITY
POSTS_TABLE_READ_CAPACITY=$DEFAULT_READ_CAPACITY
If you are trying to use a variable with a $
in the actual value, it needs to be escaped, \$
.
NAME=Spongebob
# becomes "Hi Spongebob"
GREETING=Hi $NAME
# becomes "Hi $NAME"
GREETING=Hi \$NAME
Other environment variables
The .env
environment variables will not modify an environment variable that has been previously set. So if you run the following:
NAME=Spongebob
npx sst deploy
While your .env
has.
NAME=Patrick
The .env
value will be ignored and process.env.NAME
will be set to Spongebob
.
Environment variables in Seed
The above idea also applies to environment variables that are set in Seed or other CIs. If you have an environment variable set in Seed, it'll override the one you have set in your .env
files.
Environment variables in Lambda functions
The .env
environment variables are only available in your infrastructure code.
You can also set them as Lambda environment variables by including them in the Function environment
prop:
new Function(stack, "MyFunction", {
handler: "src/api.main",
environment: {
MY_ENV_VAR: process.env.MY_ENV_VAR,
},
});
Or you can use the App's setDefaultFunctionProps
method to set it for all the functions in your app.
export default function main(app) {
app.setDefaultFunctionProps({
environment: { MY_ENV_VAR: process.env.MY_ENV_VAR },
});
new MySampleStack(app, "sample");
}
FAQ
Here are some frequently asked questions about Config
.
Should I use Config.Secret
or .env
for secrets?
Although SST supports managing secrets using .env
files, it's not recommended. Here are a couple of reasons why.
Let's take the example of a Stripe secret key. Using the .env
way, you would create a .env.local
file on your local.
STRIPE_KEY=sk_test_abc123
Since the .env.local
file is not committed to git, every team member working on the app would need to create a similar .env.local
file. And they'll need to bug you to get the value.
If you want to deploy the app through your CI pipeline, you'll need to store the STRIPE_KEY
in your CI pipeline. In addition, if you are deploying to multiple stages through your CI pipeline, each stage would need its own STRIPE_KEY
, you'd store both versions of the key (ie. STRIPE_KEY_STAGE_FOO
and STRIPE_KEY_STAGE_BAR
), and pick the one that matches the stage name at deploy time.
All these are made simpler and far more secure, with Config
. You set the secrets centrally for all stages:
npx sst secrets set STRIPE_KEY sk_test_abc123
npx sst secrets set STRIPE_KEY sk_live_xyz456 --stage foo
npx sst secrets set STRIPE_KEY sk_live_xyz789 --stage bar
You can also set a fallback value for ephemeral stages.
npx sst secrets set --fallback STRIPE_KEY sk_test_abc123
At runtime, the functions are going to pick up the correct value based on the stage, whether they are running locally, inside a test, or in production.
Finally, the Config
object in your Lambda function handles errors and is typesafe. So unlike process.env
, Config.STRIPE_KEY
will autocomplete. And an invalid secret like Config.XYZ
will throw a runtime error.