Build a Bash Lambda Runtime

Write a Bash custom runtime that runs Lambda handlers through the runtime API.

Introduction

30 Second Summary

A Run button can make code work in seconds while hiding everything that happened underneath. That convenience leaves a frustrating gap when you want to understand why it worked.

In this project, you will write a custom runtime that lets AWS Lambda execute Bash. You will use the Lambda Runtime API to reveal the request loop that managed runtimes usually hide.

What You'll Build

Send a test event from the Lambda console to watch your Bash handler return a response through the runtime loop you wrote yourself.

By the end of this project, you'll have:

  • Upload a small ZIP archive to see Lambda load your executable bootstrap on provided.al2023.
  • Send an event to receive a Bash response built from its value.
  • Trace a deliberate failure through CloudWatch logs to the runtime API call that reported it.
  • Secret Mission: adapt your reusable bootstrap template for another language that has an HTTP client.

Are there any prerequisites?

You'll need an AWS account and a browser. This project fits best if you have already deployed an ordinary Lambda function.

Before We Start

Bash sits outside the managed runtime list for AWS Lambda. A prepared AWS account gives your custom runtime somewhere to run.

A Bash terminal gives you the tools to assemble the deployment package. This step prepares that terminal before you write the runtime loop.

In this step, get ready to:
  • Set the AWS Management Console to the project Region.
  • Verify every required tool in AWS CloudShell.
  • Create the bash-lambda-runtime working folder.
Choose your AWS Region

An AWS Region is the geographic area where AWS runs your regional resources. Keeping this project in us-east-1 ensures each console page points at the same resources.

  • Sign in to the AWS Management Console.
  • Choose the name of the currently displayed Region in the top navigation bar.
  • Select US East (N. Virginia).

Later steps create resources across three AWS services. Your current role needs the matching permissions:

Permissions You Need

  • Permission to create IAM roles.
  • Permission to create Lambda functions.
  • Permission to create CloudWatch Logs resources.
  • Confirm these permissions with your AWS administrator.

Missing AWS Permissions?

Your administrator can grant the required access or provide a learning account with the permissions listed above.

Ask for help here: help me confirm which permissions my AWS account needs for this project. You can also ask the AWS community.

Your console is now anchored in us-east-1. Every regional resource in this project will appear in the same place.

Prepare your AWS CloudShell terminal

AWS CloudShell is a browser-based terminal. It uses the credentials from your console session.

  • Choose the CloudShell icon in the console navigation bar.
  • Wait for the $ command prompt to display.
  • Check the required terminal tools by running these commands:
bash --version
curl --version
zip --version
unzip --version
chmod --version

What Does This Check Prove?

  • The bash output confirms that your shell can run the bootstrap script.
  • The curl output confirms that the runtime can make HTTP requests.
  • The zip output confirms that you can build the deployment package.
  • The unzip output confirms that you can inspect archive contents.
  • The chmod output confirms that you can make the bootstrap executable.

You should see version details for all five tools. That output confirms your terminal has everything this project needs.

Missing a Command?

  • Choose Actions in AWS CloudShell.
  • Select the option to restart the session.
  • Run the tool check again after the prompt returns.

Still stuck? Help me diagnose a missing tool in AWS CloudShell.

Create your project folder

The working folder keeps the runtime files together at the root of the deployment package. Your terminal should remain inside this folder throughout the build.

  • Create the bash-lambda-runtime folder and enter it by running these commands:
mkdir bash-lambda-runtime
cd bash-lambda-runtime

What Do These Commands Do?

The mkdir command creates the project folder. The cd command makes that folder your terminal's current location.

  • Predict which folder name should appear at the end of the current path.
  • Confirm the folder location by running these commands:
pwd
ls -ld .

What Should You See?

The first line should end with /bash-lambda-runtime. The second line should describe the current directory represented by ..

Folder Check Look Different?

  • Check that the folder name uses lowercase letters with hyphens.
  • Use the existing folder only if it contains no files from another project.

Need help? Help me fix my bash-lambda-runtime working folder.

Your workspace is ready. The console points at the correct Region while your terminal sits inside the project folder with every required tool available.

✔️ Awesome, I've got everything!

Great work. Keep this terminal open inside bash-lambda-runtime for the next step.

ⓧ I'd like to double check the full code

This setup section has no source file. Its artifact is the open working folder.

  • The AWS Management Console displays US East (N. Virginia) as the current Region.
  • The terminal provides bash, curl, zip, unzip, and chmod.
  • The current terminal path ends with /bash-lambda-runtime.
  • The AWS account can create IAM roles, Lambda functions, and CloudWatch Logs resources.

Your AWS workspace is ready for the runtime itself. Next, you will write the bootstrap loop that receives Lambda events and returns each handler response.

Write the Custom Bootstrap

Managed runtimes make AWS Lambda feel automatic. That convenience hides the event loop behind each invocation.

In this step, you will turn that hidden loop into a bootstrap file written in Bash. It talks to the Lambda Runtime API directly.

In this step, get ready to:
  • Build a loop that requests each Lambda invocation.
  • Route handler results to the matching response or error endpoint.
  • Make the bootstrap executable and confirm its permission bit.
Build the invocation loop

Lambda gives a custom runtime its API address through AWS_LAMBDA_RUNTIME_API. Each request to the next-invocation endpoint waits until an event is ready.

The response headers identify that invocation. Your bootstrap uses the request ID to send the handler result back to the correct place.

  • Create bootstrap inside the open bash-lambda-runtime folder by running this command:
cat > bootstrap <<'EOF'
#!/bin/bash

set -euo pipefail

# Keep the runtime ready to process every invocation
while true; do
  # Receive the event body while saving its response headers
  HEADERS="$(mktemp)"
  EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")

  # Match the handler result to the current invocation
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # Send successful output to the response endpoint
  if RESPONSE=$(./handler.sh "$EVENT_DATA"); then
    curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
  else
    # Report a failed handler through the error endpoint
    ERROR='{"errorMessage":"handler.sh exited with an error"}'
    curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/error" -d "$ERROR"
  fi
done
EOF

What does this bootstrap do?

  • The while true loop keeps the runtime ready for repeated invocations.
  • The first curl call waits for an event from /2018-06-01/runtime/invocation/next.
  • The event body is stored in EVENT_DATA.
  • The response headers are stored in a temporary file referenced by HEADERS.
  • The Lambda-Runtime-Aws-Request-Id header becomes REQUEST_ID.
  • The success branch posts RESPONSE to the matching response endpoint.
  • The failure branch posts an error payload to the matching error endpoint.
  • Confirm the file exists by running this command:
ls -l bootstrap

What should I see?

You should see one listing that ends with bootstrap. That file now holds your complete custom runtime loop.

Bootstrap file missing?

  • Return to the terminal that is already inside bash-lambda-runtime.
  • Check that the closing EOF begins at the first character of its line.
  • Run the file creation command again after correcting the closing line.

Ask for help checking why the bootstrap file was not created.

✔️ Awesome, I've got everything!

Your bootstrap file now contains the polling loop and both result paths.

ⓧ I'd like to double check the full code

  • Compare your bootstrap file with this complete version:
#!/bin/bash

set -euo pipefail

# Keep the runtime ready to process every invocation
while true; do
  # Receive the event body while saving its response headers
  HEADERS="$(mktemp)"
  EVENT_DATA=$(curl -sS -LD "$HEADERS" "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")

  # Match the handler result to the current invocation
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # Send successful output to the response endpoint
  if RESPONSE=$(./handler.sh "$EVENT_DATA"); then
    curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response"  -d "$RESPONSE"
  else
    # Report a failed handler through the error endpoint
    ERROR='{"errorMessage":"handler.sh exited with an error"}'
    curl "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/error" -d "$ERROR"
  fi
done
Make the bootstrap executable

Lambda treats bootstrap as the custom runtime entrypoint. The file needs execute permission before Lambda can start it.

Unix permission strings are compact, so spotting the execute bit can feel fiddly. An x in the permission characters confirms that the file is executable.

Before the final check, picture where a new x permission marker should appear in the listing.

  • Add execute permission and display the updated file listing by running this command:
chmod a+x bootstrap && ls -l bootstrap

What does this command confirm?

The chmod a+x bootstrap command grants execute permission to every user class. The following ls -l bootstrap command displays the resulting permission characters.

You should see at least one x before the bootstrap file name. That confirms the executable permission bit is present.

That is the entrypoint ready: the permission listing now proves Lambda can execute bootstrap.

Execute marker still missing?

  • Confirm the listing is for the file named exactly bootstrap.
  • Check that the file name contains no extension or trailing space.
  • Run the permission command again from the open bash-lambda-runtime folder.

Ask for help diagnosing why the execute permission is missing.

Your custom runtime can now receive events and report handler results. Next, you will write the handler.sh script that turns each event into Bash output.

Create and Package the Handler

Your custom runtime can now receive an AWS Lambda invocation. The loop still needs a script that turns the event into a response.

In this step, you will write a Bash handler that reads one value from each event. You will package the handler beside bootstrap so Lambda can upload both files together.

In this step, get ready to:
  • Write a handler that reads a value from a JSON event.
  • Confirm the handler returns a response containing that value.
  • Build a small deployment archive with both executable scripts.
Write the handler

The runtime passes the complete JSON event to handler.sh as its first argument. The handler extracts the event's name value before building a greeting.

  • Create handler.sh inside the bash-lambda-runtime folder using the file editor connected to your terminal.
  • Paste the following code into handler.sh:
#!/bin/bash

# Receive the JSON event from the custom runtime
EVENT="$1"

# Extract the name field from the event payload
NAME=$(printf '%s' "$EVENT" | sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')

# Return a greeting for Lambda to send as the response
echo "Hello from Bash, $NAME!"

What Does This Code Do?

  • The EVENT variable holds the complete event passed by the runtime.
  • The sed expression extracts the string assigned to the name field.
  • The final echo prints the response that the runtime returns to Lambda.
  • Save handler.sh.

Before you test the handler, what greeting do you expect the sample event to produce?

  • Test the handler with a sample event by running this command:
bash handler.sh '{"name":"NextWork"}'

What Does This Test Prove?

The command passes a compact event directly to the handler. The printed greeting proves that the extraction logic can read the event value.

You should see Hello from Bash, NextWork! in your terminal. That successful greeting confirms your handler can process an event.

Missing the Name in the Greeting?

Check that the event uses the lowercase field name name. Confirm that the quotation marks around NextWork are present.

Compare the extraction pattern in handler.sh with the code above.

Ask for help with the extraction pattern.

✔️ Awesome, I've got everything!

Your handler returned the sample greeting. Double check that you saved handler.sh before continuing.

ⓧ I'd like to double check the full code

Compare your complete handler.sh file with this version:

#!/bin/bash

# Receive the JSON event from the custom runtime
EVENT="$1"

# Extract the name field from the event payload
NAME=$(printf '%s' "$EVENT" | sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')

# Return a greeting for Lambda to send as the response
echo "Hello from Bash, $NAME!"
Prepare the handler for direct execution

The existing bootstrap file launches handler.sh directly. The handler therefore needs its own execute permission.

  • Apply executable permissions to handler.sh by running this command:
chmod 755 handler.sh

Why Use Mode 755?

The 755 mode gives the file owner read access. It also gives the owner write access.

Every user receives execute access. This allows the runtime to launch the handler as a program.

  • Confirm that both scripts are executable by running:
ls -l bootstrap handler.sh

What Does the Listing Show?

The first column displays each file's permission bits. An x marks execute permission.

Checking both rows also confirms that the existing bootstrap permission remains intact.

You should see execute permission on the rows for bootstrap and handler.sh.

Handler Still Missing Execute Permission?

Confirm that your terminal is still inside the bash-lambda-runtime folder. The file listing should show handler.sh in this folder.

Ask for help checking the handler permission.

Assemble the deployment package

A ZIP archive carries the runtime files into Lambda. Passing both filenames directly keeps them at the archive root.

  • Place bootstrap beside handler.sh in bash-runtime.zip by running:
zip bash-runtime.zip bootstrap handler.sh

What Does This Command Package?

The archive stores the runtime entry point named bootstrap. It also stores the executable handler named handler.sh.

Both source files sit in the current folder. Their stored paths contain no extra directory names.

Archive Command Did Not Complete?

Confirm that bootstrap appears in the current folder. Confirm that handler.sh appears there too.

Ask for help creating the deployment archive.

Before you inspect the archive, do you expect its listing to show bare filenames or a containing folder?

  • Inspect the archive paths by running these commands:
zipinfo -l bash-runtime.zip
ls -lh bash-runtime.zip

What Do These Checks Prove?

  • The zipinfo listing shows each stored path.
  • Its first column shows the stored permission bits.
  • The ls listing reports the archive size in a readable unit.

You should see exactly bootstrap and handler.sh as top-level archive entries.

Both entries should show execute permission. The bootstrap permission proves the archive preserved the runtime entry point correctly.

The bash-runtime.zip row should show a size of only a few kilobytes. Your deployment package is ready for Lambda.

Seeing Folders Inside the Archive?

Remove only the incorrect local archive. Recreate it while your terminal is inside the bash-lambda-runtime folder.

Pass the two filenames directly to zip. This keeps both files at the archive root.

Ask for help fixing the archive structure.

Your handler can process an event. Next up, you will create the Lambda function that runs your packaged Bash runtime.

Deploy the Bash Lambda

Your bash-runtime.zip package now contains the executable runtime loop plus its shell handler. The next job is to give that package a home in AWS Lambda.

An IAM execution role gives the function its AWS permissions. The attached policy lets the function send its future output to CloudWatch Logs.

In this step, get ready to:
  • Create an execution role for the Bash function.
  • Create a function on the provided.al2023 runtime.
  • Deploy bash-runtime.zip to the function.
Create the execution role

An execution role is the AWS identity that Lambda assumes while your function runs. Its trust relationship controls which service can use it.

  • Switch back to the AWS Management Console from earlier.
  • Use the top search bar to search for IAM.
  • Select IAM from the search results.
  • Select Roles from the left navigation pane.
  • Select Create role.
  • Select AWS service under Trusted entity type.
  • Select Lambda under Service or use case.
  • Select Next.

Choosing Lambda as the use case adds lambda.amazonaws.com as the trusted service. This lets Lambda assume the role when your function runs.

  • Search Permissions policies for AWSLambdaBasicExecutionRole.
  • Select the checkbox beside AWSLambdaBasicExecutionRole.
  • Select Next.
  • Enter bash-custom-runtime-role in the Role name field.
  • Review that Lambda appears as the trusted entity.
  • Select Create role.

Good work. The bash-custom-runtime-role role now has the logging permission your function needs.

AWS can take a few seconds to make a new role available to Lambda. That brief delay is normal.

  • Select Permissions on the role page.
  • Confirm that AWSLambdaBasicExecutionRole appears in the policy list.
  • Select Trust relationships.
  • Confirm that the trusted service is lambda.amazonaws.com.

What does this role allow?

The AWSLambdaBasicExecutionRole managed policy lets the function create log groups. It also lets the function write log events.

The trust relationship grants Lambda permission to assume the role. Your Bash function receives only the permissions attached to that role.

Role not created?

Check whether a role named bash-custom-runtime-role already exists in your account. Role names must be unique within an AWS account.

If the policy is missing, return to Permissions on the role page. Attach AWSLambdaBasicExecutionRole before continuing.

help me troubleshoot my Lambda execution role. The NextWork community can also help with account-specific console behavior.

Create the custom-runtime function

The provided.al2023 runtime supplies the Amazon Linux environment for your package. Your executable bootstrap file supplies the runtime loop that Lambda starts.

  • Use the console search bar to search for Lambda.
  • Select Lambda from the search results.
  • Set the region picker to us-east-1.
  • Select Functions from the left navigation pane.
  • Select Create function.
  • Select Author from scratch.
  • Enter bash-custom-runtime in the Function name field.
  • Choose provided.al2023 from the Runtime menu.

Why use an OS-only runtime?

The OS-only runtime starts the executable bootstrap file at the root of your deployment package. This gives your Bash loop control over every invocation.

  • Expand Change default execution role.
  • Choose Use an existing role.
  • Select bash-custom-runtime-role from the existing-role menu.
  • Select Create function.

Function creation can take a few seconds while Lambda connects the execution role. Your browser stays on the creation page during this wait.

Your bash-custom-runtime function page loads when creation finishes. That page confirms the cloud resource now exists in us-east-1.

Can't select the execution role?

Refresh the existing-role menu if bash-custom-runtime-role does not appear. New IAM roles can take a few seconds to become available.

Confirm that the role trusts lambda.amazonaws.com if function creation is rejected.

help me connect my execution role to the Lambda function. The NextWork community can help if your account uses a different permissions layout.

The Lambda upload dialog can only select files stored on your computer. Use the tab that matches where you built bash-runtime.zip.

I used AWS CloudShell

  • Switch back to AWS CloudShell from earlier.
  • Select Actions in the CloudShell interface.
  • Select Download file.
  • Enter the full path to bash-runtime.zip inside your existing bash-lambda-runtime folder.
  • Select Download.
  • Save the downloaded archive to your computer.

I used a local terminal

Your archive is already stored on your computer. Keep the existing bash-lambda-runtime folder ready for the file picker.

  • Return to the Code tab on the function page.
  • Choose Upload from in the Code source pane.
  • Choose .zip file.
  • Choose Upload.
  • Select bash-runtime.zip from your computer.
  • Choose Open in the file picker.
  • Choose Save.

Great, the deployment package is now in place. Lambda now holds the executable bootstrap file you wrote.

  • Find Runtime settings on the Code tab.
  • Choose Edit.
  • Enter handler.sh in the Handler field.
  • Choose Save.

Why set the handler name?

Lambda keeps a Handler value for functions using a custom runtime. Setting it to handler.sh makes the function configuration match the script your bootstrap runs.

The executable bootstrap file remains the runtime entry point. It controls the handoff to handler.sh.

ZIP upload failing?

Confirm that you selected bash-runtime.zip instead of the entire bash-lambda-runtime folder.

The archive must contain bootstrap plus handler.sh at the ZIP root. A nested project folder prevents Lambda from finding the runtime entry point.

help me troubleshoot my custom runtime ZIP upload. The NextWork community can help review a screenshot of the package layout.

Verify the deployed configuration

A complete deployment needs three settings to line up. The runtime, handler name, and execution role must all point to the resources you prepared.

Before you check, which runtime, handler, and role do you expect the function page to show?

  • Return to Runtime settings on the Code tab.
  • Confirm that Runtime shows provided.al2023.
  • Confirm that Handler shows handler.sh.
  • Select the Configuration tab.
  • Select Permissions.
  • Confirm that the execution role is bash-custom-runtime-role.
  • Return to the Code tab.

That is the deployment confirmed. Your bash-custom-runtime function now uses the OS-only runtime plus the package you built.

Your Bash runtime is deployed. Next, you will send it an event and watch your shell handler return a Lambda response.

Invoke Your Bash Handler

Your AWS Lambda function is deployed with your custom runtime. It still needs a real invocation to prove that the runtime can pass an event into Bash.

A console test event gives the function a JSON payload. The successful response proves that your bootstrap loop reaches handler.sh and returns its output to Lambda.

In this step, get ready to:
  • Create a saved test event containing the value expected by handler.sh.
  • Invoke the deployed function from the Lambda console.
  • Confirm the response contains the Bash-generated output.
Create a saved test event

A saved test event is reusable JSON input. Lambda sends it to your runtime each time you run that event.

  • Switch back to the Lambda function page for bash-custom-runtime from the previous step.
  • Choose the Test tab.
  • Under Test event, choose Create new event.
  • Enter bash-custom-runtime in the Name field.
  • Under Event sharing settings, choose Private.
  • Replace the sample JSON with an object that uses the exact property extracted by handler.sh.
  • Set that property to the same test value you used when writing handler.sh.
  • Choose Save changes.

You should see bash-custom-runtime selected as the saved test event. Its JSON now matches the input shape your Bash handler consumes.

Can't save the test event?

  • Check that the event editor contains one complete JSON object.
  • Wrap the property name in double quotes.
  • Wrap a text value in double quotes.

Ask for help with the event structure if the console still rejects it: help me correct my Lambda test event JSON.

Invoke the handler and inspect the response

The console uses a synchronous invocation for this test. Lambda waits for your custom runtime to process the event before displaying the returned output.

Before you invoke the function, what part of the event do you expect to reappear in the response?

  • Choose Test to invoke the function.

You should see Executing function: succeeded when the invocation finishes.

  • Under Execution result, expand Details.

In Response, you should see the Bash-generated output containing the value from your saved test event.

You've proven the complete runtime loop. Lambda just returned output from a Bash handler running through the bootstrap you wrote.

How did the event travel?

  • The console sent your saved event to the deployed Lambda function.
  • The bootstrap file requested the invocation from the runtime API.
  • The bootstrap passed the event payload into handler.sh.
  • The bootstrap posted the handler output back as the invocation response.

Test did not return Bash output?

  • Compare the test event property with the property extracted by handler.sh.
  • Confirm that bootstrap sits at the root of bash-runtime.zip.
  • Confirm that bootstrap and handler.sh still have executable permissions in the deployment package.

Share the visible result without including credentials: help me debug why my custom Lambda runtime did not return the Bash handler output.

Your custom runtime has now handled a real event successfully. Next, you will make the handler fail on purpose and trace that failure through Amazon CloudWatch logs.

Trace a Handler Failure

Your Bash handler already returned a successful response from AWS Lambda. That result proves the runtime can complete its happy path.

A successful response leaves the failure path hidden. This step deliberately gives the handler a nonzero exit status.

You will trace the failed invocation in CloudWatch Logs. You will connect its request identifier to the Runtime API error endpoint.

In this step, get ready to:
  • Force a controlled nonzero exit from the Bash handler.
  • Inspect the failed invocation in CloudWatch Logs.
  • Connect the failure to the bootstrap error-reporting endpoint.
Trigger a controlled failure

A Bash process returns an exit status when it finishes. Status 0 signals success. A nonzero status signals failure.

This is a temporary code change. Your earlier successful invocation stays in CloudWatch Logs.

  • Switch back to the terminal from earlier.
  • Open handler.sh in your preferred text editor.
  • Append this temporary failure at the end of handler.sh:
# Force this test invocation down the runtime failure path
exit 1

What Does This Temporary Line Do?

The exit 1 command stops the handler with status 1. Bash treats that status as a failed execution.

Your bootstrap reads the handler status. A nonzero value sends the invocation through its error-reporting path.

  • Save handler.sh.
  • Rebuild bash-runtime.zip by rerunning the packaging command from the earlier step.

You should see handler.sh named in the ZIP update output. This confirms the temporary failure is inside the new package.

  • Return to the bash-custom-runtime function page from earlier.
  • Choose the Code tab.
  • Choose Upload from in the Code source pane.
  • Choose .zip file.
  • Choose Upload.
  • Select bash-runtime.zip from the bash-lambda-runtime folder.
  • Choose Open.
  • Choose Save.

You handled the temporary edit cleanly. The controlled failure is now deployed.

Before you invoke the function, what do you think the console will do with the handler's nonzero status?

  • Choose Test to invoke the saved test event.

You will see a failed invocation instead of the earlier Bash-generated response. This is the intended result of the controlled failure.

Why This Failure Matters

Lambda needs the custom runtime to report what happened inside the handler. Your bootstrap turns the nonzero exit status into a Runtime API error request.

The failed console result proves that Lambda received that report. The logs provide the request-level evidence behind it.

Function Still Succeeds?

  • Confirm that exit 1 is the final executable line in handler.sh.
  • Rerun the packaging command from the bash-lambda-runtime folder.
  • Confirm that the packaging output names handler.sh.
  • Upload the rebuilt bash-runtime.zip before testing again.

Ask for help with the deployment package if the successful response still appears: help me check why my Bash Lambda handler still succeeds after I added exit 1

Trace the failure in CloudWatch Logs

Lambda sends invocation records to CloudWatch Logs through the permissions attached to the execution role. The failed test now gives you a specific invocation to trace.

  • Choose the Monitor tab on the bash-custom-runtime function page.
  • Choose View CloudWatch logs.
  • Confirm that the log group name is /aws/lambda/bash-custom-runtime.

CloudWatch Logs can take 5 to 10 minutes to show a new invocation. A quiet page during that window is normal.

  • Reload the log streams list after a short wait if the failed invocation is missing.
  • Choose the newest log stream whose timestamp matches your failed test.

You should see records for the failed invocation. The entries include the failure details plus the request identifier assigned to that invocation.

  • Locate the failure details in the newest log stream.
  • Identify the request identifier shown with that invocation.
  • Open bootstrap from the bash-lambda-runtime folder in your preferred text editor.
  • Press Cmd+F on macOS or Ctrl+F on Windows.
  • Search for /error.

You will find the bootstrap call matching /2018-06-01/runtime/invocation/{request-id}/error. The bootstrap substitutes the captured request identifier into this path.

How Does the Evidence Connect?

The handler returns a nonzero status to bootstrap. The bootstrap posts the error to the invocation-specific endpoint.

Lambda records that invocation as failed. CloudWatch Logs preserves the failure details for investigation.

Restore the successful handler

You have traced the complete failure path. You can now remove the controlled fault while keeping its failed invocation in CloudWatch Logs.

  • Return to handler.sh in your editor.
  • Remove the temporary comment plus the exit 1 line.
  • Save handler.sh.
  • Rebuild bash-runtime.zip with the packaging command from the earlier step.

The packaging output should name both bootstrap and handler.sh. Your deployment package now contains the successful handler again.

✔️ Awesome, I've got everything!

Great. Your local handler is restored before you redeploy the package.

ⓧ I'd like to double check the full code

Your restored files and deployment package should match this final state.

  • The executable bootstrap still polls for events. It still sends successful output to the response endpoint. It still sends handler failures to the error endpoint.
  • The executable handler.sh again extracts the instructed event value. It echoes the Bash-generated response. The temporary exit 1 line is absent.
  • The bash-runtime.zip archive contains bootstrap at its root. It also contains handler.sh at its root.
  • The log group /aws/lambda/bash-custom-runtime retains the deliberately failed invocation for investigation.
  • Return to the bash-custom-runtime function page.
  • Choose the Code tab.
  • Choose Upload from in the Code source pane.
  • Choose .zip file.
  • Choose Upload.
  • Select the restored bash-runtime.zip.
  • Choose Open.
  • Choose Save.

Before you run the final check, do you expect the saved event to fail again or return its Bash-generated response?

  • Choose Test to invoke the saved event again.

You should see the successful Bash-generated output containing the event value. The earlier failed invocation remains available in /aws/lambda/bash-custom-runtime.

Invocation Still Failing?

  • Confirm that handler.sh no longer contains exit 1.
  • Confirm that you rebuilt bash-runtime.zip after saving the restored handler.
  • Confirm that the latest package upload completed before you chose Test.

Ask for help if the restored package still produces a failed invocation: help me trace why my restored Bash Lambda handler still fails

That is a strong finish. Your Bash runtime now handles successful responses while exposing failures through the Runtime API and CloudWatch Logs.

Secret mission

Trace an Invocation End to End

Follow one failed Lambda invocation from its log entry to the runtime API error report. You will use its request ID to build a three-stage trace that shows exactly where the failure travelled.

Clean Up Your Resources

Clean Up Your Resources

Your small test workload is expected to cost effectively $0. Choose whether to keep the AWS resources for more experiments, pause your work, or remove every resource.

Resources you used:

  • The bash-custom-runtime AWS Lambda function in us-east-1, including its deployed package and saved test event.
  • The /aws/lambda/bash-custom-runtime CloudWatch Logs log group containing the successful invocation and deliberate failure.
  • The bash-custom-runtime-role IAM execution role with AWSLambdaBasicExecutionRole attached.
  • The bash-lambda-runtime Bash project folder containing bootstrap, handler.sh, and bash-runtime.zip.

Keep everything running

No action needed. Choose this if you want to experiment with the custom runtime or inspect its failure logs again.

  • Keep bash-custom-runtime available for future console test events.
  • Retain /aws/lambda/bash-custom-runtime as evidence of the runtime API response and error flows.
  • Preserve bash-custom-runtime-role for the deployed function.
  • Keep bootstrap as a template for another custom runtime with an HTTP client.
  • Restore the successful behavior in handler.sh before your next successful test.

Pause - I'll come back to this later

Lambda has no background server to pause in this project. A pause means leaving the cloud resources and local files in place without sending more invocations.

  • Stop sending console test events to bash-custom-runtime.
  • Leave the function and execution role in us-east-1.
  • Keep the bash-lambda-runtime folder in its current location.
  • Return to the function page when you are ready to continue testing.

Delete - I don't want to use this again

Remove the deployed function, its logs, its execution role, and the local project folder. Delete the function first so the role is no longer attached to it.

Delete With Confidence

Deleting cloud resources can feel final. Your local copies of bootstrap and handler.sh remain available until the final folder command.

  • Return to the Lambda console from earlier.
  • Confirm the region selector still shows us-east-1.
  • Select bash-custom-runtime from the function list.
  • Use the deletion control on the function page.
  • Confirm deletion for bash-custom-runtime.
  • Return to the function list.

You should no longer see bash-custom-runtime in the function list. Its deployed package and saved test event are removed with it.

  • Return to the /aws/lambda/bash-custom-runtime log group from earlier.
  • Use the deletion control for that log group.
  • Return to the log group list.

You should no longer see /aws/lambda/bash-custom-runtime in CloudWatch Logs.

  • Go to the IAM role list in the AWS console.
  • Select bash-custom-runtime-role.
  • Use the deletion control on the role page.
  • Confirm the role deletion.
  • Return to the role list.

You should no longer see bash-custom-runtime-role in the role list. That clears the cloud side of your project.

Local Deletion Is Permanent

The next command permanently removes your project files. It targets the exact bash-lambda-runtime folder from this project.

  • Switch back to the Bash terminal from earlier.
  • Delete the project folder and list the remaining items by running these commands:
cd ..
rm -rf bash-lambda-runtime
ls

What Do These Commands Do?

  • The cd .. command moves the terminal to the folder that contains bash-lambda-runtime.
  • The rm -rf bash-lambda-runtime command removes the project folder and everything inside it.
  • The ls command lists the items that remain in the current folder.

You should no longer see bash-lambda-runtime in the output. Your local runtime files are now removed.

Still See the Project Folder?

Check that the terminal moved to the folder directly above bash-lambda-runtime. Close any editor or file browser using the folder before trying the removal again.

Ask for help diagnosing why the folder remains.

Nice Work!

Nice Work!

You did it! You built a Bash custom runtime that runs on AWS Lambda. You also traced successful and failed invocations through the Lambda Runtime API.

You've learned how to:

  • Build an executable bootstrap runtime loop that requests invocation events. You also captured request IDs before returning responses to Lambda.
  • Package an executable Bash handler with its bootstrap file. You then deployed the ZIP package using the provided.al2023 runtime.
  • Trigger a deliberate failure. You then used CloudWatch Logs to connect the handler's nonzero exit to the runtime API error-reporting call.
  • Complete an optional Secret Mission to push your custom runtime skills further.

Ready to quiz yourself?