Skip to content

Using uv with AWS LambdaAWS Lambdaでのuvの使用

AWS Lambda is a serverless computing service that lets you run code without provisioning or managing servers.AWS Lambdaは、サーバーをプロビジョニングまたは管理することなくコードを実行できるサーバーレスコンピューティングサービスです。

You can use uv with AWS Lambda to manage your Python dependencies, build your deployment package, and deploy your Lambda functions.AWS Lambdaでuvを使用して、Pythonの依存関係を管理し、デプロイメントパッケージを構築し、Lambda関数をデプロイできます。

Tipヒント

Check out the uv-aws-lambda-example project for an example of best practices when using uv to deploy an application to AWS Lambda.uv-aws-lambda-exampleプロジェクトをチェックして、uvを使用してAWS Lambdaにアプリケーションをデプロイする際のベストプラクティスの例を確認してください。

Getting startedはじめに

To start, assume we have a minimal FastAPI application with the following structure:まず、次の構造を持つ最小限のFastAPIアプリケーションがあると仮定します:

project
├── pyproject.toml
└── app
    ├── __init__.py
    └── main.py

Where the pyproject.toml contains:pyproject.toml に含まれる内容は次の通りです:

pyproject.toml
[project]
name = "uv-aws-lambda-example"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
    # FastAPI is a modern web framework for building APIs with Python.
    "fastapi",
    # Mangum is a library that adapts ASGI applications to AWS Lambda and API Gateway.
    "mangum",
]

[dependency-groups]
dev = [
    # In development mode, include the FastAPI development server.
    "fastapi[standard]>=0.115",
]

And the main.py file contains:main.py ファイルに含まれる内容は次の通りです:

app/main.py
import logging

from fastapi import FastAPI
from mangum import Mangum

logger = logging.getLogger()
logger.setLevel(logging.INFO)

app = FastAPI()
handler = Mangum(app)


@app.get("/")
async def root() -> str:
    return "Hello, world!"

We can run this application locally with:このアプリケーションをローカルで実行するには、次のコマンドを使用します:

$ uv run fastapi dev

From there, opening http://127.0.0.1:8000/ in a web browser will display "Hello, world!"そこから、ウェブブラウザで http://127.0.0.1:8000/ を開くと、「Hello, world!」が表示されます。

Deploying a Docker imageDocker イメージのデプロイ

To deploy to AWS Lambda, we need to build a container image that includes the application code and dependencies in a single output directory.AWS Lambda にデプロイするには、アプリケーションコードと依存関係を単一の出力ディレクトリに含むコンテナイメージを構築する必要があります。

We'll follow the principles outlined in the Docker guide (in particular, a multi-stage build) to ensure that the final image is as small and cache-friendly as possible.最終的なイメージができるだけ小さく、キャッシュに優しいようにするために、Docker ガイド(特にマルチステージビルド)に記載されている原則に従います。

In the first stage, we'll populate a single directory with all application code and dependencies. In the second stage, we'll copy this directory over to the final image, omitting the build tools and other unnecessary files.最初のステージでは、すべてのアプリケーションコードと依存関係を単一のディレクトリに配置します。2 番目のステージでは、このディレクトリを最終イメージにコピーし、ビルドツールやその他の不要なファイルを省きます。

Dockerfile
FROM ghcr.io/astral-sh/uv:0.9.13 AS uv

# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder

# Enable bytecode compilation, to improve cold-start performance.
ENV UV_COMPILE_BYTECODE=1

# Disable installer metadata, to create a deterministic layer.
ENV UV_NO_INSTALLER_METADATA=1

# Enable copy mode to support bind mount caching.
ENV UV_LINK_MODE=copy

# Bundle the dependencies into the Lambda task root via `uv pip install --target`.
#
# Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`).
# This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock`
# files change, but remains robust to changes in the application code.
RUN --mount=from=uv,source=/uv,target=/bin/uv \
    --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \
    uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

FROM public.ecr.aws/lambda/python:3.13

# Copy the runtime dependencies from the builder stage.
COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}

# Copy the application code.
COPY ./app ${LAMBDA_TASK_ROOT}/app

# Set the AWS Lambda handler.
CMD ["app.main.handler"]

Tipヒント

To deploy to ARM-based AWS Lambda runtimes, replace public.ecr.aws/lambda/python:3.13 with public.ecr.aws/lambda/python:3.13-arm64.ARM ベースの AWS Lambda ランタイムにデプロイするには、public.ecr.aws/lambda/python:3.13public.ecr.aws/lambda/python:3.13-arm64 に置き換えます。

We can build the image with, e.g.:例えば、次のコマンドでイメージをビルドできます:

$ uv lock
$ docker build -t fastapi-app .

The core benefits of this Dockerfile structure are as follows:このDockerfile構造の主な利点は次のとおりです:

  1. Minimal image size. By using a multi-stage build, we can ensure that the final image only includes the application code and dependencies. For example, the uv binary itself is not included in the final image.最小のイメージサイズ。 マルチステージビルドを使用することで、最終的なイメージにはアプリケーションコードと依存関係のみが含まれることを保証できます。たとえば、uvバイナリ自体は最終イメージには含まれません。
  2. Maximal cache reuse. By installing application dependencies separately from the application code, we can ensure that the Docker layer cache is only invalidated when the dependencies change.最大のキャッシュ再利用。 アプリケーションコードとは別にアプリケーション依存関係をインストールすることで、依存関係が変更されたときのみDockerレイヤーキャッシュが無効化されることを保証できます。

Concretely, rebuilding the image after modifying the application source code can reuse the cached layers, resulting in millisecond builds:具体的には、アプリケーションソースコードを変更した後にイメージを再ビルドすると、キャッシュされたレイヤーを再利用でき、ミリ秒単位のビルドが実現します:

 => [internal] load build definition from Dockerfile                                                                 0.0s
 => => transferring dockerfile: 1.31kB                                                                               0.0s
 => [internal] load metadata for public.ecr.aws/lambda/python:3.13                                                   0.3s
 => [internal] load metadata for ghcr.io/astral-sh/uv:latest                                                         0.3s
 => [internal] load .dockerignore                                                                                    0.0s
 => => transferring context: 106B                                                                                    0.0s
 => [uv 1/1] FROM ghcr.io/astral-sh/uv:latest@sha256:ea61e006cfec0e8d81fae901ad703e09d2c6cf1aa58abcb6507d124b50286f  0.0s
 => [builder 1/2] FROM public.ecr.aws/lambda/python:3.13@sha256:f5b51b377b80bd303fe8055084e2763336ea8920d12955b23ef  0.0s
 => [internal] load build context                                                                                    0.0s
 => => transferring context: 185B                                                                                    0.0s
 => CACHED [builder 2/2] RUN --mount=from=uv,source=/uv,target=/bin/uv     --mount=type=cache,target=/root/.cache/u  0.0s
 => CACHED [stage-2 2/3] COPY --from=builder /var/task /var/task                                                     0.0s
 => CACHED [stage-2 3/3] COPY ./app /var/task                                                                        0.0s
 => exporting to image                                                                                               0.0s
 => => exporting layers                                                                                              0.0s
 => => writing image sha256:6f8f9ef715a7cda466b677a9df4046ebbb90c8e88595242ade3b4771f547652d                         0.0

After building, we can push the image to Elastic Container Registry (ECR) with, e.g.:ビルド後、イメージを Elastic Container Registry (ECR)にプッシュできます。例えば:

$ aws ecr get-login-password --region region | docker login --username AWS --password-stdin aws_account_id.dkr.ecr.region.amazonaws.com
$ docker tag fastapi-app:latest aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest
$ docker push aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest

Finally, we can deploy the image to AWS Lambda using the AWS Management Console or the AWS CLI, e.g.:最後に、AWS Management ConsoleまたはAWS CLIを使用してイメージをAWS Lambdaにデプロイできます。例えば:

$ aws lambda create-function \
   --function-name myFunction \
   --package-type Image \
   --code ImageUri=aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest \
   --role arn:aws:iam::111122223333:role/my-lambda-role

Where the execution role is created via:ここで、 実行ロールは次のように作成されます:

$ aws iam create-role \
   --role-name my-lambda-role \
   --assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'

Or, update an existing function with:または、次のコマンドで既存の関数を更新します:

$ aws lambda update-function-code \
   --function-name myFunction \
   --image-uri aws_account_id.dkr.ecr.region.amazonaws.com/fastapi-app:latest \
   --publish

To test the Lambda, we can invoke it via the AWS Management Console or the AWS CLI, e.g.:Lambdaをテストするために、AWS Management ConsoleまたはAWS CLIを介して呼び出すことができます。例えば:

$ aws lambda invoke \
   --function-name myFunction \
   --payload file://event.json \
   --cli-binary-format raw-in-base64-out \
   response.json
{
  "StatusCode": 200,
  "ExecutedVersion": "$LATEST"
}

Where event.json contains the event payload to pass to the Lambda function:ここで、event.jsonにはLambda関数に渡すイベントペイロードが含まれています:

event.json
{
  "httpMethod": "GET",
  "path": "/",
  "requestContext": {},
  "version": "1.0"
}

And response.json contains the response from the Lambda function:そして response.json には、Lambda 関数からの応答が含まれています:

response.json
{
  "statusCode": 200,
  "headers": {
    "content-length": "14",
    "content-type": "application/json"
  },
  "multiValueHeaders": {},
  "body": "\"Hello, world!\"",
  "isBase64Encoded": false
}

For details, see the AWS Lambda documentation.詳細については、 AWS Lambda ドキュメント を参照してください。

Workspace supportワークスペースのサポート

If a project includes local dependencies (e.g., via Workspaces), those too must be included in the deployment package.プロジェクトにローカル依存関係が含まれている場合(例: ワークスペースを介して)、それらもデプロイメントパッケージに含める必要があります。

We'll start by extending the above example to include a dependency on a locally-developed library named library.まず、上記の例を拡張して、library という名前のローカルで開発されたライブラリへの依存関係を含めます。

First, we'll create the library itself:最初に、ライブラリ自体を作成します:

$ uv init --lib library
$ uv add ./library

Running uv init within the project directory will automatically convert project to a workspace and add library as a workspace member:project ディレクトリ内で uv init を実行すると、自動的に project がワークスペースに変換され、library がワークスペースメンバーとして追加されます:

pyproject.toml
[project]
name = "uv-aws-lambda-example"
version = "0.1.0"
requires-python = ">=3.13"
dependencies = [
    # FastAPI is a modern web framework for building APIs with Python.
    "fastapi",
    # A local library.
    "library",
    # Mangum is a library that adapts ASGI applications to AWS Lambda and API Gateway.
    "mangum",
]

[dependency-groups]
dev = [
    # In development mode, include the FastAPI development server.
    "fastapi[standard]",
]

[tool.uv.workspace]
members = ["library"]

[tool.uv.sources]
lib = { workspace = true }

By default, uv init --lib will create a package that exports a hello function. We'll modify the application source code to call that function:デフォルトでは、uv init --libhello 関数をエクスポートするパッケージを作成します。アプリケーションのソースコードを修正して、その関数を呼び出すようにします:

app/main.py
import logging

from fastapi import FastAPI
from mangum import Mangum

from library import hello

logger = logging.getLogger()
logger.setLevel(logging.INFO)

app = FastAPI()
handler = Mangum(app)


@app.get("/")
async def root() -> str:
    return hello()

We can run the modified application locally with:修正されたアプリケーションをローカルで実行できます:

$ uv run fastapi dev

And confirm that opening http://127.0.0.1:8000/ in a web browser displays, "Hello from library!" (instead of "Hello, World!")そして、ウェブブラウザで http://127.0.0.1:8000/ を開くと、「Hello from library!」が表示されることを確認します(「Hello, World!」の代わりに)。

Finally, we'll update the Dockerfile to include the local library in the deployment package:最後に、デプロイメントパッケージにローカルライブラリを含めるためにDockerfileを更新します:

Dockerfile
FROM ghcr.io/astral-sh/uv:0.9.13 AS uv

# First, bundle the dependencies into the task root.
FROM public.ecr.aws/lambda/python:3.13 AS builder

# Enable bytecode compilation, to improve cold-start performance.
ENV UV_COMPILE_BYTECODE=1

# Disable installer metadata, to create a deterministic layer.
ENV UV_NO_INSTALLER_METADATA=1

# Enable copy mode to support bind mount caching.
ENV UV_LINK_MODE=copy

# Bundle the dependencies into the Lambda task root via `uv pip install --target`.
#
# Omit any local packages (`--no-emit-workspace`) and development dependencies (`--no-dev`).
# This ensures that the Docker layer cache is only invalidated when the `pyproject.toml` or `uv.lock`
# files change, but remains robust to changes in the application code.
RUN --mount=from=uv,source=/uv,target=/bin/uv \
    --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv export --frozen --no-emit-workspace --no-dev --no-editable -o requirements.txt && \
    uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

# If you have a workspace, copy it over and install it too.
#
# By omitting `--no-emit-workspace`, `library` will be copied into the task root. Using a separate
# `RUN` command ensures that all third-party dependencies are cached separately and remain
# robust to changes in the workspace.
RUN --mount=from=uv,source=/uv,target=/bin/uv \
    --mount=type=cache,target=/root/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    --mount=type=bind,source=library,target=library \
    uv export --frozen --no-dev --no-editable -o requirements.txt && \
    uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"

FROM public.ecr.aws/lambda/python:3.13

# Copy the runtime dependencies from the builder stage.
COPY --from=builder ${LAMBDA_TASK_ROOT} ${LAMBDA_TASK_ROOT}

# Copy the application code.
COPY ./app ${LAMBDA_TASK_ROOT}/app

# Set the AWS Lambda handler.
CMD ["app.main.handler"]

Tipヒント

To deploy to ARM-based AWS Lambda runtimes, replace public.ecr.aws/lambda/python:3.13 with public.ecr.aws/lambda/python:3.13-arm64.ARMベースのAWS Lambdaランタイムにデプロイするには、public.ecr.aws/lambda/python:3.13public.ecr.aws/lambda/python:3.13-arm64に置き換えます。

From there, we can build and deploy the updated image as before.そこから、以前と同様に更新されたイメージをビルドしてデプロイできます。

Deploying a zip archiveZIPアーカイブのデプロイ

AWS Lambda also supports deployment via zip archives. For simple applications, zip archives can be a more straightforward and efficient deployment method than Docker images; however, zip archives are limited to 250 MB in size.AWS LambdaはZIPアーカイブによるデプロイもサポートしています。シンプルなアプリケーションの場合、ZIPアーカイブはDockerイメージよりも より簡単で効率的なデプロイ方法となることがあります。ただし、ZIPアーカイブは 250 MB にサイズが制限されています。

Returning to the FastAPI example, we can bundle the application dependencies into a local directory for AWS Lambda via:FastAPIの例に戻ると、AWS Lambda用にアプリケーションの依存関係をローカルディレクトリにバンドルできます:

$ uv export --frozen --no-dev --no-editable -o requirements.txt
$ uv pip install \
   --no-installer-metadata \
   --no-compile-bytecode \
   --python-platform x86_64-manylinux2014 \
   --python 3.13 \
   --target packages \
   -r requirements.txt

Tipヒント

To deploy to ARM-based AWS Lambda runtimes, replace x86_64-manylinux2014 with aarch64-manylinux2014.ARMベースのAWS Lambdaランタイムにデプロイするには、x86_64-manylinux2014aarch64-manylinux2014に置き換えます。

Following the AWS Lambda documentation, we can then bundle these dependencies into a zip as follows:次に、 AWS Lambdaのドキュメントに従って、これらの依存関係をZIPにバンドルできます:

$ cd packages
$ zip -r ../package.zip .
$ cd ..

Finally, we can add the application code to the zip archive:最後に、アプリケーションコードをZIPアーカイブに追加できます:

$ zip -r package.zip app

We can then deploy the zip archive to AWS Lambda via the AWS Management Console or the AWS CLI, e.g.:その後、AWS Management ConsoleまたはAWS CLIを介してZIPアーカイブをAWS Lambdaにデプロイできます。 e.g.:

$ aws lambda create-function \
   --function-name myFunction \
   --runtime python3.13 \
   --zip-file fileb://package.zip \
   --handler app.main.handler \
   --role arn:aws:iam::111122223333:role/service-role/my-lambda-role

Where the execution role is created via:どこで 実行ロール が作成されます:

$ aws iam create-role \
   --role-name my-lambda-role \
   --assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'

Or, update an existing function with:または、次のコマンドで既存の関数を更新します:

$ aws lambda update-function-code \
   --function-name myFunction \
   --zip-file fileb://package.zip

Note注意

By default, the AWS Management Console assumes a Lambda entrypoint of lambda_function.lambda_handler. If your application uses a different entrypoint, you'll need to modify it in the AWS Management Console. For example, the above FastAPI application uses app.main.handler.デフォルトでは、AWS Management ConsoleはLambdaエントリポイントをlambda_function.lambda_handlerとして想定します。 アプリケーションが異なるエントリポイントを使用している場合は、AWS Management Consoleでそれを変更する必要があります。 例えば、上記のFastAPIアプリケーションはapp.main.handlerを使用しています。

To test the Lambda, we can invoke it via the AWS Management Console or the AWS CLI, e.g.:Lambdaをテストするには、AWS Management ConsoleまたはAWS CLIを介して呼び出すことができます。例えば:

$ aws lambda invoke \
   --function-name myFunction \
   --payload file://event.json \
   --cli-binary-format raw-in-base64-out \
   response.json
{
  "StatusCode": 200,
  "ExecutedVersion": "$LATEST"
}

Where event.json contains the event payload to pass to the Lambda function:ここでevent.jsonはLambda関数に渡すイベントペイロードを含みます:

event.json
{
  "httpMethod": "GET",
  "path": "/",
  "requestContext": {},
  "version": "1.0"
}

And response.json contains the response from the Lambda function:そしてresponse.jsonはLambda関数からの応答を含みます:

response.json
{
  "statusCode": 200,
  "headers": {
    "content-length": "14",
    "content-type": "application/json"
  },
  "multiValueHeaders": {},
  "body": "\"Hello, world!\"",
  "isBase64Encoded": false
}

Using a Lambda layerLambdaレイヤーの使用

AWS Lambda also supports the deployment of multiple composed Lambda layers when working with zip archives. These layers are conceptually similar to layers in a Docker image, allowing you to separate application code from dependencies.AWS Lambdaは、複数の構成された Lambdaレイヤーのデプロイもサポートしています。 これらのレイヤーは、Dockerイメージのレイヤーに概念的に似ており、アプリケーションコードと依存関係を分離することができます。

In particular, we can create a lambda layer for application dependencies and attach it to the Lambda function, separate from the application code itself. This setup can improve cold-start performance for application updates, as the dependencies layer can be reused across deployments.特に、アプリケーション依存関係のためのLambdaレイヤーを作成し、それをLambda関数にアタッチすることができます。 これにより、アプリケーションコード自体とは別に、コールドスタートパフォーマンスを向上させることができます。 依存関係のレイヤーはデプロイ間で再利用できるためです。

To create a Lambda layer, we'll follow similar steps, but create two separate zip archives: one for the application code and one for the application dependencies.Lambdaレイヤーを作成するには、同様の手順に従いますが、アプリケーションコード用とアプリケーション依存関係用の2つの別々のzipアーカイブを作成します。

First, we'll create the dependency layer. Lambda layers are expected to follow a slightly different structure, so we'll use --prefix rather than --target:まず、依存関係レイヤーを作成します。Lambdaレイヤーは少し異なる構造に従うことが期待されるため、--prefixを使用し、--targetは使用しません。

$ uv export --frozen --no-dev --no-editable -o requirements.txt
$ uv pip install \
   --no-installer-metadata \
   --no-compile-bytecode \
   --python-platform x86_64-manylinux2014 \
   --python 3.13 \
   --prefix packages \
   -r requirements.txt

We'll then zip the dependencies in adherence with the expected layout for Lambda layers:次に、Lambdaレイヤーの期待されるレイアウトに従って依存関係をzip圧縮します。

$ mkdir python
$ cp -r packages/lib python/
$ zip -r layer_content.zip python

Tipヒント

To generate deterministic zip archives, consider passing the -X flag to zip to exclude extended attributes and file system metadata.決定論的なzipアーカイブを生成するには、zip-Xフラグを渡して、拡張属性やファイルシステムメタデータを除外することを検討してください。

And publish the Lambda layer:そして、Lambdaレイヤーを公開します。

$ aws lambda publish-layer-version --layer-name dependencies-layer \
   --zip-file fileb://layer_content.zip \
   --compatible-runtimes python3.13 \
   --compatible-architectures "x86_64"

We can then create the Lambda function as in the previous example, omitting the dependencies:次に、前の例と同様に依存関係を省略してLambda関数を作成できます。

$ # Zip the application code.
$ zip -r app.zip app

$ # Create the Lambda function.
$ aws lambda create-function \
   --function-name myFunction \
   --runtime python3.13 \
   --zip-file fileb://app.zip \
   --handler app.main.handler \
   --role arn:aws:iam::111122223333:role/service-role/my-lambda-role

Finally, we can attach the dependencies layer to the Lambda function, using the ARN returned by the publish-layer-version step:最後に、publish-layer-versionステップから返されたARNを使用して、依存関係レイヤーをLambda関数に添付できます。

$ aws lambda update-function-configuration --function-name myFunction \
    --cli-binary-format raw-in-base64-out \
    --layers "arn:aws:lambda:region:111122223333:layer:dependencies-layer:1"

When the application dependencies change, the layer can be updated independently of the application by republishing the layer and updating the Lambda function configuration:アプリケーションの依存関係が変更された場合、レイヤーを再公開し、Lambda関数の設定を更新することで、アプリケーションとは独立してレイヤーを更新できます。

$ # Update the dependencies in the layer.
$ aws lambda publish-layer-version --layer-name dependencies-layer \
   --zip-file fileb://layer_content.zip \
   --compatible-runtimes python3.13 \
   --compatible-architectures "x86_64"

$ # Update the Lambda function configuration.
$ aws lambda update-function-configuration --function-name myFunction \
    --cli-binary-format raw-in-base64-out \
    --layers "arn:aws:lambda:region:111122223333:layer:dependencies-layer:2"