Deploy a Lambda Container Image With ECR and the Console
You built a QR code Lambda and ran it locally. Now put it on AWS the click-through way: create an Amazon ECR repository, push your image, and create the Lambda from that image in the console. Then test it and optionally expose it with a Function URL your Laravel app can call.
In the previous part we built a QR code Lambda, ran it locally in Docker, and called it from Laravel, all without an AWS account. Now we put it online. This part is mostly clicking through the AWS Console, with a couple of Docker commands you have already run. You will need an AWS account, the AWS CLI configured (aws configure), and Docker running with the qr-generator image you built last time.
The path is short: store the image in Amazon ECR (Elastic Container Registry, AWS's private Docker registry), then create a Lambda from it.
Create an ECR repository
A repository is just a named home for one image and its tags.
- Open the Amazon ECR console and pick your Region (top right). Remember which one; it has to match your function later.
- Choose Private repositories, then Create repository.
- For Repository name, enter
qr-generator(lowercase; it may include a namespace liketeam/qr-generator). - For Image tag immutability, leave it Mutable so you can re-push
:latestwhile iterating. - Leave Encryption on the default AES-256.
- Choose Create.
That is the whole repository. It is empty until we push an image into it.
Push your image
Select the new repository and choose View push commands. The console prints four commands with your account ID and Region already filled in. They log Docker in to your registry, then build, tag, and push:
aws ecr get-login-password --region us-east-1 \
| docker login --username AWS --password-stdin 111122223333.dkr.ecr.us-east-1.amazonaws.com
docker build -t qr-generator .
docker tag qr-generator:latest 111122223333.dkr.ecr.us-east-1.amazonaws.com/qr-generator:latest
docker push 111122223333.dkr.ecr.us-east-1.amazonaws.com/qr-generator:latest
Swap 111122223333 for your account ID (the console does this for you). You already built the image in the previous part, so the docker build step just confirms it is current. When docker push finishes, refresh the repository in the console and you will see your image with the latest tag.
Create the Lambda from the image
Now point a function at that image.
- Open the Lambda console and choose Create function.
- Pick Container image (not "Author from scratch").
- For Function name, enter
qr-generator. - For Container image URI, choose Browse images, select the
qr-generatorrepository, pick thelatesttag, and choose Select image. - For Architecture, choose the one your image was built for. On an Apple Silicon Mac that is arm64; on an Intel machine it is x86_64. It has to match the image, because a Lambda image is built for exactly one architecture.
- Choose Create function.
A few things to know. The ECR repository must be in the same Region as the function. Right after you create it, the function sits in a Pending state for a few seconds while Lambda optimizes the image, then flips to Active; you cannot invoke it until it is Active. And Lambda needs permission to pull the image: when the repository is in the same account, it adds that permission for you automatically as long as your user is allowed to set repository policies.
Configure and test
Open the function. Under the Configuration tab you can bump Memory (try 512 MB) and Timeout (30 seconds is plenty), though the defaults work for our tiny function.
Then the satisfying part. Go to the Test tab, create a new test event, and give it the same shape we used locally:
{ "data": "https://franktheprogrammer.com" }
Choose Test. You will see the function's JSON response, including the qr_base64 field, plus the execution logs and the duration. That is your container, running on AWS, producing the same result it did on your laptop.
Going further: a Function URL your Laravel app can call
The console Test is great, but your Laravel app wants a real endpoint. The simplest one is a Function URL, a dedicated HTTPS address for this function with no API Gateway needed.
- On the function, open the Configuration tab, choose Function URL, then Create function URL.
- For Auth type, choose NONE for a quick public demo (anyone with the URL can call it, so treat it as semi-secret), or AWS_IAM to require signed requests.
- Optionally set CORS, then Save. You get a URL like
https://abc123.lambda-url.us-east-1.amazonaws.com.
A Function URL delivers the request as an HTTP event, which wraps your posted JSON in a body string rather than passing it as the top-level event. Two extra lines make the handler accept both shapes, so it still works for the console Test and an SDK call:
import base64
import io
import json
import qrcode
def handler(event, context):
# A Function URL wraps the POST body as a JSON string; a direct invoke passes it as-is.
if event.get("body"):
event = json.loads(event["body"])
data = event.get("data", "https://franktheprogrammer.com")
img = qrcode.make(data)
buffer = io.BytesIO()
img.save(buffer, format="PNG")
return {
"data": data,
"qr_base64": base64.b64encode(buffer.getvalue()).decode(),
}
Rebuild and push the image, redeploy (see below), and Laravel just swaps the local emulator URL for the Function URL:
use Illuminate\Support\Facades\Http;
$response = Http::post('https://abc123.lambda-url.us-east-1.amazonaws.com', [
'data' => 'https://franktheprogrammer.com',
]);
$qr = $response->json('qr_base64');
Returning a plain array from the handler lets the Function URL auto-wrap it as a 200 JSON response, so $response->json('qr_base64') reads exactly like it did locally. If you want the endpoint to return the raw PNG instead (so hitting the URL in a browser shows the image), the reference Give Your Lambda an HTTP Front Door shows the response shape for that.
Updating later
When you change the code, rebuild and push the image again (the same push commands), then tell the function to pick it up: on the function, choose Deploy new image (under the Image section), Browse images, select the new image, and Save. A tag like latest resolves to a specific image version at deploy time, so pushing alone does not update a running function; the redeploy is what repoints it.
That is the whole journey: a function you wrote and ran on your laptop is now live on AWS, testable in the console, and callable from Laravel over HTTPS. Clicking through the console is the best way to understand what each piece is. Once it feels familiar, the reference AWS Lambda series shows how to do all of this from the command line and automate it with infrastructure as code, so you never have to click it again.