Lambda Health Check


Out-of-date or misconfigured Lambdas are one of the most common and annoying failure modes in serverless apps. Common causes include:

CDK skips deployments (“no changes”) even when your code has changed, which is particularly common in mono repos where package changes are not considered when diffing.

The lambda is lacking the configuration or permissions required to operate.

The Solution

The following describes a simple, repeatable pattern that gives you confidence every Lambda is running the version you expect and talking to what it needs to:

Shared version map: Track a version number for every Lambda in a map and ensure this is accessible across the codebase. I typically use mono-repos, so this would be kept in a shared package.

All lambdas support standard health check message: Each Lambda accepts a health message and reports its name, version and optional resource checks.

System health orchestrator: A single Lambda or service (with permission to access the other lambdas) queries all others and aggregates their status. This allow the individual lambdas to remain private.

Frontend comparison: Your dashboard imports the same version map and highlights mismatches immediately.

Implementation Specifics

Version map

// shared/lambdaVersions.ts
export const LambdaVersions = {
  CreateRequest: 3,
  ProcessGeneration: 12,
  GetStatus: 4,
} as const;

Health Check Handler

As used inside each lambda

import { LambdaVersions } from "../shared/lambdaVersions";

const LAMBDA_NAME = "ProcessGeneration";

export const handler = async (event) => {
  if (event.action === "health") {
    // Perform any resource checks, e.g. connecting to a DB or pinging another service
    return {
      statusCode: 200,
      body: JSON.stringify({
        lambdaName: LAMBDA_NAME,
        version: LambdaVersions[LAMBDA_NAME],
        isHealthy: true,
      }),
    };
  }

  // normal Lambda logic...
};

Central Check

As provided by an health-check lambda or your main services:

import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda";
import { LambdaVersions } from "@myMono/shared/lambdaVersions";

const lambda = new LambdaClient({});
const lambdaNames = Object.keys(LambdaVersions);

async function check(functionName) {
  const res = await lambda.send(
    new InvokeCommand({
      FunctionName: functionName,
      Payload: JSON.stringify({ action: "health" }),
    })
  );

  return JSON.parse(new TextDecoder().decode(res.Payload)).body;
}

export const handler = async () => {
  const results = await Promise.all(lambdaNames.map(check));

  return {
    statusCode: 200,
    body: JSON.stringify({ lambdas: results }),
  };
};

Frontend Visualisation

As located in an admin app

import { LambdaVersions } from "@myMono/shared/lambdaVersions";

function VersionRow({ name, reported }) {
  const expected = LambdaVersions[name];
  const mismatch = expected !== reported;

  return (
    <div className={mismatch ? "warning" : "ok"}>
      {name}: {reported} {mismatch && `(expected ${expected})`}
    </div>
  );
}