Deploy a Serverless App to AWS Lambda
Deploy a Serverless app with an encrypted .env.vault file to AWS Lambda.
Find a complete code example on GitHub for this guide.
Initial setup
Create a serverless project.
npx serverless
Here are the options I selected for this guide. Modify for your own needs.
? What do you want to make? AWS - Node.js - Starter
? What do you want to call this project? aws-lambda
? What org do you want to add this service to? dotenv
? What application do you want to add this to? [create a new app]
? What do you want to name this application? aws-lambda
✔ Your project is ready to be deployed to Serverless Dashboard (org: "dotenv", app: "aws-lambda")
? Do you want to deploy now? Yes
Deploying aws-lambda to stage dev (us-east-1, "default" provider)
Edit index.js
to include process.env.HELLO
.
index.js
module.exports.handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify(
{
message: `Hello ${process.env.HELLO}`,
input: event,
},
null,
2
),
};
};
Deploy that to the cloud.
npx serverless deploy
Invoke your function.
Request
npx serverless invoke --function function1
Response
{
"statusCode": 200,
"body": "{\n \"message\": \"Hello undefined\",\n \"input\": {}\n}"
}
It will respond with Hello undefined
as it doesn't have a way to access the environment variable yet. Let's do that next.
Install dotenv
Install dotenv.
npm install dotenv --save # Requires dotenv >= 16.1.0
Create a .env
file in the root of your project.
.env
# .env
HELLO="World"
As early as possible in your function, import and configure dotenv.
index.js
require('dotenv').config()
console.log(process.env) // remove this after you've confirmed it is working
module.exports.handler = async (event) => {
...
Test that it is working by invoking your function locally.
npx serverless invoke local --function function1
It should say Hello World
.
{
"statusCode": 200,
"body": "{\n \"message\": \"Hello World\",\n \"input\": {}\n}"
}
Great! process.env
now has the keys and values you defined in your .env
file. That covers local development. Let's solve for production next.
Build .env.vault
Push your latest .env
file changes and edit your production secrets. Learn more about syncing
npx dotenv-vault@latest push
npx dotenv-vault@latest open production
Use the UI to configure those secrets per environment.
Then build your encrypted .env.vault
file.
npx dotenv-vault@latest build
Its contents should look something like this.
.env.vault
#/-------------------.env.vault---------------------/
#/ cloud-agnostic vaulting standard /
#/ [how it works](https://dotenv.org/env-vault) /
#/--------------------------------------------------/
# development
DOTENV_VAULT_DEVELOPMENT="/HqNgQWsf6Oh6XB9pI/CGkdgCe6d4/vWZHgP50RRoDTzkzPQk/xOaQs="
DOTENV_VAULT_DEVELOPMENT_VERSION=2
# production
DOTENV_VAULT_PRODUCTION="x26PuIKQ/xZ5eKrYomKngM+dO/9v1vxhwslE/zjHdg3l+H6q6PheB5GVDVIbZg=="
DOTENV_VAULT_PRODUCTION_VERSION=2
Set DOTENV_KEY
Fetch your production DOTENV_KEY
.
npx dotenv-vault@latest keys production
# outputs: dotenv://:[email protected]/vault/.env.vault?environment=production
Edit your serverless.yml
file to inject the DOTENV_KEY
as a param.
serverless.yml
...
provider:
name: aws
runtime: nodejs18.x
environment:
DOTENV_KEY: ${param:DOTENV_KEY}
...
Important, additionally, edit your serverless.yml
file to ignore your .env*
files excepting your .env.vault
file.
serverless.yml
...
package:
patterns:
- '!.env*'
- '.env.vault'
When complete, your serverless.yml
file should look something like this.
serverless.yml
org: dotenv
app: aws-lambda
service: aws-lambda
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs18.x
environment:
DOTENV_KEY: ${param:DOTENV_KEY}
functions:
function1:
handler: index.handler
package:
patterns:
- '!.env*'
- '.env.vault'
Using the DOTENV_KEY
you fetched above, set it as param on the Serverless dashboard.
Deploy to the cloud again.
npx serverless deploy
Now invoke your function.
Request
npx serverless invoke --function function1
That's it! On invocation, your .env.vault
file will be decrypted and its production secrets injected as environment variables – just-in-time.
Response
{
"statusCode": 200,
"body": "{\n \"message\": \"Hello production\",\n \"input\": {}\n}"
}
You succesfully used the new .env.vault standard to encrypt and deploy your secrets. This is much safer than scattering your secrets across multiple third-party platforms and tools. Whenever you need to add or change a secret, just rebuild your .env.vault file and redeploy.