SDKs & Integrations
AWS Lambda Layer
Pre-built Lambda Layer that fetches secrets from KeyEnv at cold start.
AWS Lambda Layer
Pre-built Lambda Layer that automatically fetches secrets from KeyEnv and injects them into your Lambda function's environment variables.
Features
- Fetches secrets once at cold start (minimal latency impact)
- Caches secrets in memory for warm invocations
- Supports Node.js 18/20 and Python 3.9/3.10/3.11/3.12
- Automatic retry with exponential backoff
- Falls back to existing env vars if KeyEnv is unreachable
- Zero code changes required
Layer ARN
arn:aws:lambda:<region>:123456789012:layer:keyenv:<version>Available in all AWS regions. Replace <region> with your region (e.g., us-east-1).
Quick Start
- Add the KeyEnv layer to your Lambda function
- Set environment variables:
KEYENV_TOKEN: Your service tokenKEYENV_ENVIRONMENT: Environment name (e.g.,production)
- Deploy - secrets are automatically loaded
Environment Variables
| Variable | Description | Required | Default |
|---|---|---|---|
KEYENV_TOKEN | KeyEnv service token | Yes | - |
KEYENV_ENVIRONMENT | Environment name | Yes | - |
KEYENV_PROJECT_ID | Project ID. Optional if using project-scoped token. | No | - |
KEYENV_API_URL | KeyEnv API URL | No | https://api.keyenv.dev |
KEYENV_CACHE_TTL | Cache TTL in seconds (0 = cache forever) | No | 0 |
Examples
AWS Console
- Open your Lambda function in the AWS Console
- Go to Layers > Add a layer
- Choose Specify an ARN
- Enter:
arn:aws:lambda:us-east-1:123456789012:layer:keyenv:1 - Add environment variables in Configuration > Environment variables
Serverless Framework
# serverless.yml
service: my-service
provider:
name: aws
runtime: nodejs20.x
layers:
- arn:aws:lambda:${aws:region}:123456789012:layer:keyenv:1
environment:
KEYENV_TOKEN: ${ssm:/keyenv/token}
KEYENV_ENVIRONMENT: ${opt:stage, 'development'}
functions:
api:
handler: handler.main
events:
- http:
path: /
method: getAWS SAM
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs20.x
Layers:
- arn:aws:lambda:!Ref AWS::Region:123456789012:layer:keyenv:1
Environment:
Variables:
KEYENV_TOKEN: !Ref KeyEnvToken
KEYENV_ENVIRONMENT: !Ref Environment
Parameters:
KeyEnvToken:
Type: AWS::SSM::Parameter::Value<String>
Default: /keyenv/token
Environment:
Type: String
Default: development
Resources:
ApiFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
CodeUri: ./src
Events:
Api:
Type: Api
Properties:
Path: /
Method: GETTerraform
# main.tf
resource "aws_lambda_function" "api" {
function_name = "my-api"
runtime = "nodejs20.x"
handler = "index.handler"
filename = "function.zip"
layers = [
"arn:aws:lambda:${var.region}:123456789012:layer:keyenv:1"
]
environment {
variables = {
KEYENV_TOKEN = var.keyenv_token
KEYENV_ENVIRONMENT = var.environment
}
}
}
variable "region" {
default = "us-east-1"
}
variable "keyenv_token" {
sensitive = true
}
variable "environment" {
default = "development"
}AWS CDK (TypeScript)
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class MyStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const keyenvLayer = lambda.LayerVersion.fromLayerVersionArn(
this, 'KeyEnvLayer',
`arn:aws:lambda:${this.region}:123456789012:layer:keyenv:1`
);
new lambda.Function(this, 'ApiFunction', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('src'),
layers: [keyenvLayer],
environment: {
KEYENV_TOKEN: process.env.KEYENV_TOKEN!,
KEYENV_ENVIRONMENT: 'production',
},
});
}
}AWS CDK (Python)
from aws_cdk import (
Stack,
aws_lambda as lambda_,
)
from constructs import Construct
class MyStack(Stack):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
keyenv_layer = lambda_.LayerVersion.from_layer_version_arn(
self, "KeyEnvLayer",
f"arn:aws:lambda:{self.region}:123456789012:layer:keyenv:1"
)
lambda_.Function(
self, "ApiFunction",
runtime=lambda_.Runtime.PYTHON_3_12,
handler="index.handler",
code=lambda_.Code.from_asset("src"),
layers=[keyenv_layer],
environment={
"KEYENV_TOKEN": os.environ["KEYENV_TOKEN"],
"KEYENV_ENVIRONMENT": "production",
},
)Pulumi (TypeScript)
import * as aws from "@pulumi/aws";
const keyenvLayerArn = `arn:aws:lambda:${aws.config.region}:123456789012:layer:keyenv:1`;
const apiFunction = new aws.lambda.Function("api", {
runtime: "nodejs20.x",
handler: "index.handler",
code: new pulumi.asset.FileArchive("./src"),
layers: [keyenvLayerArn],
environment: {
variables: {
KEYENV_TOKEN: config.requireSecret("keyenvToken"),
KEYENV_ENVIRONMENT: "production",
},
},
});How It Works
- Cold Start: When Lambda initializes, the layer's extension runs first
- Fetch Secrets: The extension calls KeyEnv API to fetch secrets
- Inject: Secrets are injected into the process environment variables
- Your Code Runs: Your handler sees secrets as regular env vars
- Warm Invocations: Cached secrets are reused (no API call)
┌─────────────────────────────────────────────────────┐
│ Cold Start │
├─────────────────────────────────────────────────────┤
│ 1. Lambda Runtime Initializes │
│ 2. KeyEnv Layer Extension Runs │
│ └─> Fetches secrets from KeyEnv API │
│ └─> Injects into process.env │
│ 3. Your Handler Initializes │
│ 4. Handler Invoked │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Warm Invocation │
├─────────────────────────────────────────────────────┤
│ 1. Handler Invoked (secrets already in process.env)│
└─────────────────────────────────────────────────────┘Performance
| Scenario | Latency Impact |
|---|---|
| Cold start (first invocation) | +50-150ms |
| Warm invocation | 0ms (cached) |
| KeyEnv unreachable | 0ms (fallback to existing env vars) |
The layer is optimized for minimal cold start impact:
- Single HTTP request to KeyEnv API
- Response caching for warm invocations
- Graceful fallback if API is unreachable
Using Secrets in Your Code
Node.js
// Secrets are automatically available in process.env
export const handler = async (event) => {
const databaseUrl = process.env.DATABASE_URL;
const apiKey = process.env.API_KEY;
// Use secrets normally
const db = await connectToDatabase(databaseUrl);
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello!' }),
};
};Python
import os
def handler(event, context):
# Secrets are automatically available in os.environ
database_url = os.environ['DATABASE_URL']
api_key = os.environ['API_KEY']
# Use secrets normally
db = connect_to_database(database_url)
return {
'statusCode': 200,
'body': json.dumps({'message': 'Hello!'})
}Cache TTL
By default, secrets are cached forever for the lifetime of the Lambda instance. To refresh secrets periodically:
# serverless.yml
environment:
KEYENV_CACHE_TTL: "300" # Refresh every 5 minutes| TTL Value | Behavior |
|---|---|
0 (default) | Cache forever (best performance) |
300 | Refresh every 5 minutes |
3600 | Refresh every hour |
Setting Up Your Token
- Go to your KeyEnv dashboard
- Navigate to Settings > Service Tokens
- Create a new token with:
- Scope: Select your project (recommended)
- Permissions:
secrets:read
- Store the token securely:
- Recommended: AWS Systems Manager Parameter Store (SecureString)
- Alternative: AWS Secrets Manager
Using SSM Parameter Store
# Store token
aws ssm put-parameter \
--name /keyenv/token \
--value "your-service-token" \
--type SecureString
# Reference in serverless.yml
environment:
KEYENV_TOKEN: ${ssm:/keyenv/token}Security
- The service token should be stored in SSM Parameter Store or Secrets Manager
- Secrets are transmitted over HTTPS
- Secrets are cached in memory only (not written to disk)
- Lambda execution role needs no additional permissions
Supported Runtimes
| Runtime | Supported |
|---|---|
| Node.js 20.x | Yes |
| Node.js 18.x | Yes |
| Python 3.12 | Yes |
| Python 3.11 | Yes |
| Python 3.10 | Yes |
| Python 3.9 | Yes |
| Other runtimes | Use SDK instead |
Self-Hosted KeyEnv
If you're running a self-hosted KeyEnv instance:
environment:
KEYENV_API_URL: https://keyenv.your-company.comTroubleshooting
"Secrets not loading"
- Check CloudWatch logs for extension errors
- Verify
KEYENV_TOKENis set correctly - Ensure the Lambda has internet access (VPC configuration)
"Authentication failed"
- Verify your token is correct
- Check the token hasn't expired
- Ensure the token is stored correctly in SSM/environment
"Access denied"
- The token may not have access to the specified project
- The token may not have access to the specified environment
- Check token permissions in KeyEnv dashboard
"Timeout during initialization"
- The KeyEnv API may be unreachable
- Check VPC and security group configuration
- Ensure NAT Gateway or VPC endpoints are configured
"High cold start latency"
- This is expected (~50-150ms overhead)
- Consider using Provisioned Concurrency for latency-sensitive functions
- The overhead only applies to cold starts