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 '@mantleframework/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:

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: {
    repositoryPrefix: 'my-prefix',     // default: name_prefix from core module
    tagStrategy: 'content-hash',       // 'content-hash' | 'git-sha' | 'timestamp'
    imageRetentionCount: 10,           // default: 5
  },
})

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