Skip to content

ECR Container Lambdas ​

Deploy Lambda functions as Docker container images instead of ZIP bundles — useful for binaries, large dependencies, or custom runtimes.

Define a Container Lambda ​

Use defineLambda with packageType: 'container' in a standalone Lambda:

typescript
// src/lambdas/standalone/DownloadMedia/index.ts
import { defineLambda } from "@j0nathan-ll0yd/core";

defineLambda({
  packageType: "container",
  dockerfile: "Dockerfile.download", // relative to project root
  architecture: "x86_64", // or 'arm64' (default)
  memorySize: 2048,
  timeout: 900,
  dockerBuildArgs: {
    YTDLP_VERSION: "2026.03.25",
    DENO_VERSION: "2.2.0",
  },
  dockerTarget: "production", // multi-stage target (optional)
});

export const handler = async (event: unknown) => {
  // handler logic
  return { statusCode: 200 };
};

Write a Dockerfile ​

The build step runs esbuild first, then calls docker build. Your Dockerfile receives the compiled index.mjs in the build output directory:

dockerfile
FROM public.ecr.aws/lambda/nodejs:22

# Install runtime dependencies
RUN dnf install -y yt-dlp

# Copy compiled handler
COPY build/lambdas/DownloadMedia/index.mjs ${LAMBDA_TASK_ROOT}/

CMD ["index.handler"]

Build ​

bash
mantle build

mantle build detects container Lambdas and requires Docker to be running. It:

  1. Runs esbuild (same as ZIP — produces index.mjs)
  2. Computes a content hash of the Dockerfile + build output
  3. Runs docker build --platform linux/amd64|arm64 -t <name>:<hash>
  4. Writes a .image-tag file to build/lambdas/<Name>/

Deploy ​

bash
mantle deploy --stage staging

mantle deploy automatically authenticates with ECR, tags the image, and pushes before running OpenTofu:

text
aws ecr get-login-password | docker login ...
docker tag downloadmedia:<hash> <account>.dkr.ecr.<region>.amazonaws.com/<prefix>/download-media:<hash>
docker push ...
tofu apply -var image_uri_download_media=<full-uri> ...

Generated Infrastructure ​

mantle generate infra produces a .tf file per container Lambda with:

  • aws_ecr_repository — IMMUTABLE tags, scan-on-push enabled
  • aws_ecr_lifecycle_policy — retains last N images (default: 5)
  • Lambda module block with package_type = "Image" and image_uri from a Terraform variable
hcl
resource "aws_ecr_repository" "download_media" {
  name                 = "${module.core.name_prefix}/download-media"
  image_tag_mutability = "IMMUTABLE"
  force_delete         = true

  image_scanning_configuration {
    scan_on_push = true
  }

  tags = module.core.common_tags
}

Optional: Registry Configuration ​

typescript
// mantle.config.ts
export default defineConfig({
  name: "my-api",
  containerRegistry: {
    imageRetentionCount: 10, // default: 5
  },
});

imageRetentionCount is the only key. Image tags are always the build-context content hash, which is what makes redeploying unchanged bytes a no-op against an IMMUTABLE-tagged repository. tagStrategy, repositoryPrefix, and platforms were declared in MantleConfig and read nowhere in the CLI; they were removed rather than left advertising capabilities that did not exist. To record the commit an artifact came from, use Deployment Provenance, which binds the source revision without giving up content-addressed tags.

Constraints ​

  • Container Lambdas cannot use layers — layers are ZIP-only (warning emitted at build time)
  • Container Lambdas cannot use edge: true — Lambda@Edge does not support container images (build fails)
  • dockerfile path is relative to the project root, not the Lambda directory