KeyEnvKeyEnv
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

  1. Add the KeyEnv layer to your Lambda function
  2. Set environment variables:
    • KEYENV_TOKEN: Your service token
    • KEYENV_ENVIRONMENT: Environment name (e.g., production)
  3. Deploy - secrets are automatically loaded

Environment Variables

VariableDescriptionRequiredDefault
KEYENV_TOKENKeyEnv service tokenYes-
KEYENV_ENVIRONMENTEnvironment nameYes-
KEYENV_PROJECT_IDProject ID. Optional if using project-scoped token.No-
KEYENV_API_URLKeyEnv API URLNohttps://api.keyenv.dev
KEYENV_CACHE_TTLCache TTL in seconds (0 = cache forever)No0

Examples

AWS Console

  1. Open your Lambda function in the AWS Console
  2. Go to Layers > Add a layer
  3. Choose Specify an ARN
  4. Enter: arn:aws:lambda:us-east-1:123456789012:layer:keyenv:1
  5. 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: get

AWS 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: GET

Terraform

# 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

  1. Cold Start: When Lambda initializes, the layer's extension runs first
  2. Fetch Secrets: The extension calls KeyEnv API to fetch secrets
  3. Inject: Secrets are injected into the process environment variables
  4. Your Code Runs: Your handler sees secrets as regular env vars
  5. 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

ScenarioLatency Impact
Cold start (first invocation)+50-150ms
Warm invocation0ms (cached)
KeyEnv unreachable0ms (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 ValueBehavior
0 (default)Cache forever (best performance)
300Refresh every 5 minutes
3600Refresh every hour

Setting Up Your Token

  1. Go to your KeyEnv dashboard
  2. Navigate to Settings > Service Tokens
  3. Create a new token with:
    • Scope: Select your project (recommended)
    • Permissions: secrets:read
  4. 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

RuntimeSupported
Node.js 20.xYes
Node.js 18.xYes
Python 3.12Yes
Python 3.11Yes
Python 3.10Yes
Python 3.9Yes
Other runtimesUse SDK instead

Self-Hosted KeyEnv

If you're running a self-hosted KeyEnv instance:

environment:
  KEYENV_API_URL: https://keyenv.your-company.com

Troubleshooting

"Secrets not loading"

  • Check CloudWatch logs for extension errors
  • Verify KEYENV_TOKEN is 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

On this page