Skip to content

Run lambda function inside docker in Amplify gen 2

0

I would like to run my handler function inside a docker to take advantage of libreoffice and convert a docx file to PDF. I have the following resource.ts for my lambda function:

import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { defineFunction } from "@aws-amplify/backend";
import { Duration } from "aws-cdk-lib";
import { Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";

// Get the directory of the current file, which is where the Dockerfile should be.
const functionDir = path.dirname(fileURLToPath(import.meta.url));

export const docxToPdf = defineFunction(
    (scope) =>
        new Function(scope, "docx-to-pdf", {
            // This handler value is a formality for FROM_IMAGE runtime.
            // The actual entry point is the CMD in your Dockerfile.
            handler: "index.handler",

            // Specify that the runtime is a custom Docker image.
            runtime: Runtime.FROM_IMAGE,

            // Set a longer timeout and increase memory, as LibreOffice can be resource-intensive.
            timeout: Duration.seconds(45),
            memorySize: 1024, // You may need to increase this to 2048 or more.

            // Tell CDK to build the Docker image from the Dockerfile in this directory
            // and use it as the function's code and runtime.
            code: Code.fromDockerBuild(functionDir, {
                platform: 'linux/amd64',
            }),
        })
);

and a Dockerfile:

FROM public.ecr.aws/x4k0r1b8/lambda-libreoffice-base:25.2-node22-x86_64

# Set the working directory to what CDK expects for asset staging
WORKDIR /asset

# Copy only the necessary code and package files
COPY handler.ts ./
COPY package.json ./

# Install dependencies and build the handler
RUN npm install
RUN npm run build

# Set the command for Lambda
CMD [ "index.handler" ]

but when building with npx ampx sandbox, I get the following error:

[+] Building 1.7s (12/12) FINISHED                                                                                                                                            docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                          0.0s
 => => transferring dockerfile: 491B                                                                                                                                                          0.0s
 => [internal] load metadata for public.ecr.aws/x4k0r1b8/lambda-libreoffice-base:25.2-node22-x86_64                                                                                           1.6s
 => [auth] aws:: x4k0r1b8/lambda-libreoffice-base:pull token for public.ecr.aws                                                                                                               0.0s
 => [internal] load .dockerignore                                                                                                                                                             0.0s
 => => transferring context: 2B                                                                                                                                                               0.0s
 => [1/6] FROM public.ecr.aws/x4k0r1b8/lambda-libreoffice-base:25.2-node22-x86_64@sha256:1bbccb409c5c319ffe64ac411e70a2efa72a1b23d16aec2a0d35a2519a5bfe01                                     0.0s
 => => resolve public.ecr.aws/x4k0r1b8/lambda-libreoffice-base:25.2-node22-x86_64@sha256:1bbccb409c5c319ffe64ac411e70a2efa72a1b23d16aec2a0d35a2519a5bfe01                                     0.0s
 => [internal] load build context                                                                                                                                                             0.0s
 => => transferring context: 213B                                                                                                                                                             0.0s
 => CACHED [2/6] WORKDIR /asset                                                                                                                                                               0.0s
 => CACHED [3/6] COPY handler.ts ./                                                                                                                                                           0.0s
 => CACHED [4/6] COPY package.json ./                                                                                                                                                         0.0s
 => CACHED [5/6] RUN npm install                                                                                                                                                              0.0s
 => CACHED [6/6] RUN npm run build                                                                                                                                                            0.0s
 => exporting to image                                                                                                                                                                        0.0s
 => => exporting layers                                                                                                                                                                       0.0s
 => => writing image sha256:e9dab3526fb08ef28d071f66931a13fd317559f697a18c8d044be59fbcc8ea43                                                                                                  0.0s
 => => naming to docker.io/library/cdk-bd087f1b3effa5f6671e654977afff6e51e2656bb2726356543327d169007ab6                                                                                       0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/x35i8ltc9l46u7wa4tiamuod3

What's next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview 
Successfully copied 9.78MB to /private/var/folders/xp/3hdr9q512vbdv0hmdf7wsm5w0000gn/T/cdk-docker-cp-mULUba
3472cc423165dc4db62e650029272caf61b849b9b39cadade40bac5cdfb92ca3
3:16:27 PM ✔ Backend synthesized in 8.98 seconds
3:16:33 PM ✔ Type checks completed in 6.07 seconds
3:16:33 PM [ERROR] [BackendBuildError] Unable to deploy due to CDK Assembly Error
  ∟ Caused by: [AssemblyError] Assembly builder failed
    ∟ Caused by: [CustomFunctionProviderError] Failed to instantiate custom function provider
      ∟ Caused by: [ValidationError] runtime must be `Runtime.FROM_IMAGE` when using image asset for Lambda function
    Resolution: Check the definition of your custom function provided in `defineFunction` and refer to the logs for more information. See https://docs.amplify.aws/react/build-a-backend/functions/custom-functions for more details.
Resolution: Check the Caused by error and fix any issues in your backend code

What am I missing? my runtime is set to Runtime.FROM_IMAGE why is it complaining about that?

asked a year ago213 views
2 Answers
0

The error you're encountering is related to the Dockerfile structure, not your CDK integration approach. Your use of Runtime.FROM_IMAGE with Code.fromDockerBuild() is correct for container images in Amplify Gen 2.

Root Cause

AWS Lambda container images must follow specific conventions. The main issue is that your Dockerfile uses /asset as the working directory instead of the Lambda-required ${LAMBDA_TASK_ROOT}.

Solution

Updated Dockerfile

FROM public.ecr.aws/x4k0r1b8/lambda-libreoffice-base:25.2-node22-x86_64

# Copy package files to Lambda task root
COPY package*.json ${LAMBDA_TASK_ROOT}/

# Install dependencies
WORKDIR ${LAMBDA_TASK_ROOT}
RUN npm ci --only=production

# Copy and build TypeScript source
COPY handler.ts ./
COPY tsconfig.json ./
RUN npm install typescript @types/node
RUN npx tsc

# Set the handler (must match the handler parameter in your Function definition)
CMD [ "handler.handler" ]

Updated Resource Definition

import * as path from "node:path";
import { fileURLToPath } from "node:url";
import { defineFunction } from "@aws-amplify/backend";
import { Duration } from "aws-cdk-lib";
import { Code, Function, Runtime } from "aws-cdk-lib/aws-lambda";

const functionDir = path.dirname(fileURLToPath(import.meta.url));

export const docxToPdf = defineFunction(
    (scope) =>
        new Function(scope, "docx-to-pdf", {
            handler: "handler.handler", // Must match CMD in Dockerfile
            runtime: Runtime.FROM_IMAGE,
            timeout: Duration.seconds(45),
            memorySize: 1024,
            code: Code.fromDockerBuild(functionDir, {
                platform: 'linux/amd64',
            }),
        })
);

Key Points

  1. Your CDK approach is correct - Using defineFunction with CDK constructs is the proper way to deploy container images in Amplify Gen 2
  2. Use ${LAMBDA_TASK_ROOT} - This is where Lambda expects your code to be located
  3. Match handler names - The handler parameter in your Function definition must match the CMD in your Dockerfile
  4. Build in container - Install dependencies and compile TypeScript inside the container for consistency

The standard Amplify Gen 2 defineFunction({ name: "..." }) pattern does not support container images - you need the CDK escape hatch approach you're already using.

answered 10 months ago
  • Thanks for your answer. I've tried your solution but I get the following error:

    2:42:24 PM [ERROR] [BackendBuildError] Unable to deploy due to CDK Assembly Error
      ∟ Caused by: [AssemblyError] Assembly builder failed
        ∟ Caused by: [CustomFunctionProviderDockerError] Failed to instantiate custom function provider
          ∟ Caused by: [ExecutionError] Failed to copy files from 0288a4cebdc81b1d38f471bd0d302abd274f1ceee382a41e6cbd6b16547b56a3:/asset/. to /private/var/folders/xp/3hdr9q512vbdv0hmdf7wsm5w0000gn/T/cdk-docker-cp-pRE2xi: ExecutionError: docker exited with status 1
          --> Command: docker cp "0288a4cebdc81b1d38f471bd0d302abd274f1ceee382a41e6cbd6b16547b56a3:/asset/." "/private/var/folders/xp/3hdr9q512vbdv0hmdf7wsm5w0000gn/T/cdk-docker-cp-pRE2xi"
    

    It's looking in the /asset folder, not in the ${LAMBDA_TASK_ROOT}

    Have you tried it yourself?

-1

It looks like there's an issue with how the Docker image is being specified for your Lambda function in Amplify Gen 2. The error message suggests that while you've correctly set runtime: Runtime.FROM_IMAGE, there might be a mismatch in how you're providing the Docker image code.

When using container images with Lambda in Amplify Gen 2, you need to ensure that the way you're specifying the code is compatible with the runtime type. The Code.fromDockerBuild() method might not be properly recognized in this context.

For Amplify Gen 2 functions, the approach is typically more streamlined. Instead of using the CDK constructs directly, you might want to try the Amplify Gen 2 way of defining functions:

  1. First, define your function using the standard Amplify Gen 2 pattern:
import { defineFunction } from "@aws-amplify/backend";

export const docxToPdf = defineFunction({
name: "docxToPdf",
});
  1. Then create a handler.ts file with your function logic.

If you specifically need to use a Docker container with LibreOffice, you might need to:

  1. Build your Docker image separately
  2. Push it to Amazon ECR
  3. Reference the image URI in your function definition

Alternatively, you could try modifying your current approach by ensuring that all the CDK constructs are properly imported and used. Make sure you're using the latest version of the Amplify libraries and that your Docker build configuration is correct.

The error specifically mentions validation of the runtime when using an image asset, which suggests there might be an incompatibility between how you're specifying the Docker image and the runtime configuration.
Sources
Amplify Functions: Create serverless functions using TypeScript, powered by AWS Lambda | Front-End Web & Mobile
Deploy Lambda functions with container images - AWS Prescriptive Guidance
Deploy Python Lambda functions with container images - AWS Lambda

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.