dockerdocker-composelambdaaws-cdklocalstack

Hot reload Lambda functions using a dockerized LocalStack, TypeScript and AWS CDK v2


I am using LocalStack Docker and AWS CDK v2 image and I want to hot reload Lambda functions after file saves

services:
  backend:
    container_name: "${LOCALSTACK_DOCKER_NAME:-backend}"
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"            # LocalStack Edge Proxy
      - "127.0.0.1:4510-4559:4510-4559"  # External services ports
      - "127.0.0.1:8080:8080"            # LocalStack Web UI (optional)
      - "127.0.0.1:4000:4000"            # LocalStack Web UI (optional)
    environment:
      - DEBUG=1
      - SERVICES=dynamodb,lambda,apigateway,appsync,events,ssm,secretsmanager,s3,sqs,sns,cloudformation,iam,logs
      - DEFAULT_REGION=us-east-1
      - LAMBDA_DOCKER_NETWORK=app_network  # Critical for Lambda networking
      - HOSTNAME_EXTERNAL=backend   # For local service discovery
      - DOCKER_HOST=unix:///var/run/docker.sock
      - START_WEB=1             # Explicitly enable Web UI
      - PERSISTENCE=1           # Enable data persistence
      - LOCALSTACK_HOST=0.0.0.0
      - LOCALSTACK_MOUNT_CODE=true
      - LAMBDA_EXECUTOR=docker-reuse
      - LAMBDA_REMOTE_DOCKER=false
      - LAMBDA_RELOAD_CODE=True  # 👈 Critical new line
      - LAMBDA_CODE_MOUNT_DIR=/var/task  # Correct Lambda directory
      - LAMBDA_REMOVE_CONTAINERS=True  # 👈 Destroy containers after each invoke
      - LAMBDA_DOCKER_FLUSH=1          # Force fresh pulls
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./functions/build:/var/task:rw,cached"  # macOS/Windows optimization
    networks:
      - app_network

I am using this script to generate minified code inside /functions/build:

    "watch": "esbuild functions/*.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outdir=functions/build --watch"

This is the function constructor

new CustomFunction(scope, id, {
    runtime,
    environment: props.environment,
    code: Code.fromAsset(LAMBDA_MOUNT_DIR),
    handler: `${fileName}.${handler}`,
})

This is how you instantiate it

const fnHello = new lambda.NodejsFunction(this, "FnHello", {
  handler: "handler",
  entry: path.join(__dirname, "../functions/hello.ts"),
  environment: {
    TABLE_NAME: table.tableName,
  },
});

I tried those options

- "./functions/build:/var/task"
- "./functions/build:/var/task:rw"
- "./functions/build:/var/task:rw,cached"

and also tweaked many values inside docker-compose.yml

The result generates the correct files /functions/build

- functions
  - build
    - hello.js
    - hello.js.map

And inside /var/task in the backend Docker container (which is the LocalStack one), it maps the files

- var
 - task
   - hello.js
   - hello.js.map

It even gets the newest updates, so when I change the code of /functions/hello.ts it updates /var/task/hello.js inside the Docker container

The problem is that when I invoke the function, I get the old response.


Solution

  • AWS CDK, when it creates the Lambda using Code.fromAsset(), packages the code into a ZIP archive during deployment. This ZIP is uploaded to LocalStack (which mocks S3 behind the scenes), and from then on, LocalStack uses that uploaded copy of your code, not the updated file on disk.

    Once deployed to LocalStack, the Lambda gets snapshotted into the LocalStack state and is no longer linked to the live filesystem. That's why when you invoke the function, you get the old response.

    What you can do is: deploy once the lambda, and don't redeploy the CDK after that: update only the file. Use the update-function-code in the existing Lambda code using awslocal:

    awslocal lambda update-function-code \
      --function-name yourFunction \
      --zip-file fileb://functions/build/hello.zip
    

    Why? Because redeploying via CDK re-uploads the code, and defeats the point of hot reloading. The above snippet tells LocalStack: replace the code for this Lambda with the new ZIP I give you.

    Reference:

    https://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-code.html