Deploy Node.js Lambda functions with container images - AWS Lambda

Deploy Node.js Lambda functions with container images

There are three ways to build a container image for a Node.js Lambda function:

Tip

To reduce the time it takes for Lambda container functions to become active, see Use multi-stage builds in the Docker documentation. To build efficient container images, follow the Best practices for writing Dockerfiles.

This page explains how to build, test, and deploy container images for Lambda.

AWS base images for Node.js

AWS provides the following base images for Node.js:

Tags Runtime Operating system Dockerfile Deprecation

20

Node.js 20 Amazon Linux 2023 Dockerfile for Node.js 20 on GitHub

18

Node.js 18 Amazon Linux 2 Dockerfile for Node.js 18 on GitHub

16

Node.js 16 Amazon Linux 2 Dockerfile for Node.js 16 on GitHub

Jun 12, 2024

Amazon ECR repository: gallery.ecr.aws/lambda/nodejs

The Node.js 20 and later base images are based on the Amazon Linux 2023 minimal container image. Earlier base images use Amazon Linux 2. AL2023 provides several advantages over Amazon Linux 2, including a smaller deployment footprint and updated versions of libraries such as glibc.

AL2023-based images use microdnf (symlinked as dnf) as the package manager instead of yum, which is the default package manager in Amazon Linux 2. microdnf is a standalone implementation of dnf. For a list of packages that are included in AL2023-based images, refer to the Minimal Container columns in Comparing packages installed on Amazon Linux 2023 Container Images. For more information about the differences between AL2023 and Amazon Linux 2, see Introducing the Amazon Linux 2023 runtime for AWS Lambda on the AWS Compute Blog.

Note

To run AL2023-based images locally, including with AWS Serverless Application Model (AWS SAM), you must use Docker version 20.10.10 or later.

Using an AWS base image for Node.js

To complete the steps in this section, you must have the following:

To create a container image from an AWS base image for Node.js
  1. Create a directory for the project, and then switch to that directory.

    mkdir example cd example
  2. Create a new Node.js project with npm. To accept the default options provided in the interactive experience, press Enter.

    npm init
  3. Create a new file called index.js. You can add the following sample function code to the file for testing, or use your own.

    Example CommonJS handler
    exports.handler = async (event) => { const response = { statusCode: 200, body: JSON.stringify('Hello from Lambda!'), }; return response; };
  4. If your function depends on libraries other than the AWS SDK for JavaScript, use npm to add them to your package.

  5. Create a new Dockerfile with the following configuration:

    Example Dockerfile
    FROM public.ecr.aws/lambda/nodejs:20 # Copy function code COPY index.js ${LAMBDA_TASK_ROOT} # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile) CMD [ "index.handler" ]
  6. Build the Docker image with the docker build command. The following example names the image docker-image and gives it the test tag.

    docker build --platform linux/amd64 -t docker-image:test .
    Note

    The command specifies the --platform linux/amd64 option to ensure that your container is compatible with the Lambda execution environment regardless of the architecture of your build machine. If you intend to create a Lambda function using the ARM64 instruction set architecture, be sure to change the command to use the --platform linux/arm64 option instead.

  1. Start the Docker image with the docker run command. In this example, docker-image is the image name and test is the tag.

    docker run --platform linux/amd64 -p 9000:8080 docker-image:test

    This command runs the image as a container and creates a local endpoint at localhost:9000/2015-03-31/functions/function/invocations.

    Note

    If you built the Docker image for the ARM64 instruction set architecture, be sure to use the --platform linux/arm64 option instead of --platform linux/amd64.

  2. From a new terminal window, post an event to the local endpoint.

    Linux/macOS

    In Linux and macOS, run the following curl command:

    curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'

    This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

    curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
    PowerShell

    In PowerShell, run the following Invoke-WebRequest command:

    Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"

    This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

    Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
  3. Get the container ID.

    docker ps
  4. Use the docker kill command to stop the container. In this command, replace 3766c4ab331c with the container ID from the previous step.

    docker kill 3766c4ab331c
To upload the image to Amazon ECR and create the Lambda function
  1. Run the get-login-password command to authenticate the Docker CLI to your Amazon ECR registry.

    • Set the --region value to the AWS Region where you want to create the Amazon ECR repository.

    • Replace 111122223333 with your AWS account ID.

    aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
  2. Create a repository in Amazon ECR using the create-repository command.

    aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
    Note

    The Amazon ECR repository must be in the same AWS Region as the Lambda function.

    If successful, you see a response like this:

    { "repository": { "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world", "registryId": "111122223333", "repositoryName": "hello-world", "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world", "createdAt": "2023-03-09T10:39:01+00:00", "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": true }, "encryptionConfiguration": { "encryptionType": "AES256" } } }
  3. Copy the repositoryUri from the output in the previous step.

  4. Run the docker tag command to tag your local image into your Amazon ECR repository as the latest version. In this command:

    • Replace docker-image:test with the name and tag of your Docker image.

    • Replace <ECRrepositoryUri> with the repositoryUri that you copied. Make sure to include :latest at the end of the URI.

    docker tag docker-image:test <ECRrepositoryUri>:latest

    Example:

    docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
  5. Run the docker push command to deploy your local image to the Amazon ECR repository. Make sure to include :latest at the end of the repository URI.

    docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
  6. Create an execution role for the function, if you don't already have one. You need the Amazon Resource Name (ARN) of the role in the next step.

  7. Create the Lambda function. For ImageUri, specify the repository URI from earlier. Make sure to include :latest at the end of the URI.

    aws lambda create-function \ --function-name hello-world \ --package-type Image \ --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \ --role arn:aws:iam::111122223333:role/lambda-ex
    Note

    You can create a function using an image in a different AWS account, as long as the image is in the same Region as the Lambda function. For more information, see Amazon ECR cross-account permissions.

  8. Invoke the function.

    aws lambda invoke --function-name hello-world response.json

    You should see a response like this:

    { "ExecutedVersion": "$LATEST", "StatusCode": 200 }
  9. To see the output of the function, check the response.json file.

To update the function code, you must build the image again, upload the new image to the Amazon ECR repository, and then use the update-function-code command to deploy the image to the Lambda function.

Using an alternative base image with the runtime interface client

If you use an OS-only base image or an alternative base image, you must include the runtime interface client in your image. The runtime interface client extends the Lambda runtime API, which manages the interaction between Lambda and your function code.

Install the Node.js runtime interface client using the npm package manager:

npm install aws-lambda-ric

You can also download the Node.js runtime interface client from GitHub. The runtime interface client supports the following Node.js versions:

  • 14.x

  • 16.x

  • 18.x

  • 20.x

The following example demonstrates how to build a container image for Node.js using a non-AWS base image. The example Dockerfile uses a buster base image. The Dockerfile includes the runtime interface client.

To complete the steps in this section, you must have the following:

To create a container image from a non-AWS base image
  1. Create a directory for the project, and then switch to that directory.

    mkdir example cd example
  2. Create a new Node.js project with npm. To accept the default options provided in the interactive experience, press Enter.

    npm init
  3. Create a new file called index.js. You can add the following sample function code to the file for testing, or use your own.

    Example CommonJS handler
    exports.handler = async (event) => { const response = { statusCode: 200, body: JSON.stringify('Hello from Lambda!'), }; return response; };
  4. Create a new Dockerfile. The following Dockerfile uses a buster base image instead of an AWS base image. The Dockerfile includes the runtime interface client, which makes the image compatible with Lambda. The Dockerfile uses a multi-stage build. The first stage creates a build image, which is a standard Node.js environment where the function's dependencies are installed. The second stage creates a slimmer image which includes the function code and its dependencies. This reduces the final image size.

    • Set the FROM property to the base image identifier.

    • Use the COPY command to copy the function code and runtime dependencies.

    • Set the ENTRYPOINT to the module that you want the Docker container to run when it starts. In this case, the module is the runtime interface client.

    • Set the CMD argument to the Lambda function handler.

    Example Dockerfile
    # Define custom function directory ARG FUNCTION_DIR="/function" FROM node:20-buster as build-image # Include global arg in this stage of the build ARG FUNCTION_DIR # Install build dependencies RUN apt-get update && \ apt-get install -y \ g++ \ make \ cmake \ unzip \ libcurl4-openssl-dev # Copy function code RUN mkdir -p ${FUNCTION_DIR} COPY . ${FUNCTION_DIR} WORKDIR ${FUNCTION_DIR} # Install Node.js dependencies RUN npm install # Install the runtime interface client RUN npm install aws-lambda-ric # Grab a fresh slim copy of the image to reduce the final size FROM node:20-buster-slim # Required for Node runtimes which use npm@8.6.0+ because # by default npm writes logs under /home/.npm and Lambda fs is read-only ENV NPM_CONFIG_CACHE=/tmp/.npm # Include global arg in this stage of the build ARG FUNCTION_DIR # Set working directory to function root directory WORKDIR ${FUNCTION_DIR} # Copy in the built dependencies COPY --from=build-image ${FUNCTION_DIR} ${FUNCTION_DIR} # Set runtime interface client as default command for the container runtime ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"] # Pass the name of the function handler as an argument to the runtime CMD ["index.handler"]
  5. Build the Docker image with the docker build command. The following example names the image docker-image and gives it the test tag.

    docker build --platform linux/amd64 -t docker-image:test .
    Note

    The command specifies the --platform linux/amd64 option to ensure that your container is compatible with the Lambda execution environment regardless of the architecture of your build machine. If you intend to create a Lambda function using the ARM64 instruction set architecture, be sure to change the command to use the --platform linux/arm64 option instead.

Use the runtime interface emulator to locally test the image. You can build the emulator into your image or install it on your local machine.

To install and run the runtime interface emulator on your local machine
  1. From your project directory, run the following command to download the runtime interface emulator (x86-64 architecture) from GitHub and install it on your local machine.

    Linux/macOS
    mkdir -p ~/.aws-lambda-rie && \ curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \ chmod +x ~/.aws-lambda-rie/aws-lambda-rie

    To install the arm64 emulator, replace the GitHub repository URL in the previous command with the following:

    https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
    PowerShell
    $dirPath = "$HOME\.aws-lambda-rie" if (-not (Test-Path $dirPath)) { New-Item -Path $dirPath -ItemType Directory } $downloadLink = "https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie" $destinationPath = "$HOME\.aws-lambda-rie\aws-lambda-rie" Invoke-WebRequest -Uri $downloadLink -OutFile $destinationPath

    To install the arm64 emulator, replace the $downloadLink with the following:

    https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie-arm64
  2. Start the Docker image with the docker run command. Note the following:

    • docker-image is the image name and test is the tag.

    • /usr/local/bin/npx aws-lambda-ric index.handler is the ENTRYPOINT followed by the CMD from your Dockerfile.

    Linux/macOS
    docker run --platform linux/amd64 -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \ --entrypoint /aws-lambda/aws-lambda-rie \ docker-image:test \ /usr/local/bin/npx aws-lambda-ric index.handler
    PowerShell
    docker run --platform linux/amd64 -d -v "$HOME\.aws-lambda-rie:/aws-lambda" -p 9000:8080 ` --entrypoint /aws-lambda/aws-lambda-rie ` docker-image:test ` /usr/local/bin/npx aws-lambda-ric index.handler

    This command runs the image as a container and creates a local endpoint at localhost:9000/2015-03-31/functions/function/invocations.

    Note

    If you built the Docker image for the ARM64 instruction set architecture, be sure to use the --platform linux/arm64 option instead of --platform linux/amd64.

  3. Post an event to the local endpoint.

    Linux/macOS

    In Linux and macOS, run the following curl command:

    curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'

    This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

    curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"hello world!"}'
    PowerShell

    In PowerShell, run the following Invoke-WebRequest command:

    Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{}' -ContentType "application/json"

    This command invokes the function with an empty event and returns a response. If you're using your own function code rather than the sample function code, you might want to invoke the function with a JSON payload. Example:

    Invoke-WebRequest -Uri "http://localhost:9000/2015-03-31/functions/function/invocations" -Method Post -Body '{"payload":"hello world!"}' -ContentType "application/json"
  4. Get the container ID.

    docker ps
  5. Use the docker kill command to stop the container. In this command, replace 3766c4ab331c with the container ID from the previous step.

    docker kill 3766c4ab331c
To upload the image to Amazon ECR and create the Lambda function
  1. Run the get-login-password command to authenticate the Docker CLI to your Amazon ECR registry.

    • Set the --region value to the AWS Region where you want to create the Amazon ECR repository.

    • Replace 111122223333 with your AWS account ID.

    aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
  2. Create a repository in Amazon ECR using the create-repository command.

    aws ecr create-repository --repository-name hello-world --region us-east-1 --image-scanning-configuration scanOnPush=true --image-tag-mutability MUTABLE
    Note

    The Amazon ECR repository must be in the same AWS Region as the Lambda function.

    If successful, you see a response like this:

    { "repository": { "repositoryArn": "arn:aws:ecr:us-east-1:111122223333:repository/hello-world", "registryId": "111122223333", "repositoryName": "hello-world", "repositoryUri": "111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world", "createdAt": "2023-03-09T10:39:01+00:00", "imageTagMutability": "MUTABLE", "imageScanningConfiguration": { "scanOnPush": true }, "encryptionConfiguration": { "encryptionType": "AES256" } } }
  3. Copy the repositoryUri from the output in the previous step.

  4. Run the docker tag command to tag your local image into your Amazon ECR repository as the latest version. In this command:

    • Replace docker-image:test with the name and tag of your Docker image.

    • Replace <ECRrepositoryUri> with the repositoryUri that you copied. Make sure to include :latest at the end of the URI.

    docker tag docker-image:test <ECRrepositoryUri>:latest

    Example:

    docker tag docker-image:test 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
  5. Run the docker push command to deploy your local image to the Amazon ECR repository. Make sure to include :latest at the end of the repository URI.

    docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
  6. Create an execution role for the function, if you don't already have one. You need the Amazon Resource Name (ARN) of the role in the next step.

  7. Create the Lambda function. For ImageUri, specify the repository URI from earlier. Make sure to include :latest at the end of the URI.

    aws lambda create-function \ --function-name hello-world \ --package-type Image \ --code ImageUri=111122223333.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest \ --role arn:aws:iam::111122223333:role/lambda-ex
    Note

    You can create a function using an image in a different AWS account, as long as the image is in the same Region as the Lambda function. For more information, see Amazon ECR cross-account permissions.

  8. Invoke the function.

    aws lambda invoke --function-name hello-world response.json

    You should see a response like this:

    { "ExecutedVersion": "$LATEST", "StatusCode": 200 }
  9. To see the output of the function, check the response.json file.

To update the function code, you must build the image again, upload the new image to the Amazon ECR repository, and then use the update-function-code command to deploy the image to the Lambda function.