Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions examples/aws-python-container/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,125 @@ authors = [
requires-python = "==3.11.*"
```

## Docker Build Options

SST supports several Docker build options for Python container functions, powered by `@pulumi/docker-build`.

### Build Args

You can pass build arguments to the Docker build using `buildArgs`. This is useful for passing configuration values needed during the Docker build that can be baked into the image.

```typescript title="sst.config.ts"
const withBuildArgs = new sst.aws.Function("MyPythonFunction", {
python: {
container: true,
buildArgs: {
MY_BUILD_ARG: "my-value",
},
},
handler: "build_args/src/build_args/api.handler",
runtime: "python3.11",
url: true
});
```

In your Dockerfile, declare the ARG after the FROM statement to make it available in the build stage:

```dockerfile title="build_args/Dockerfile"
ARG PYTHON_VERSION=3.11
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}

# Declare build args AFTER FROM to make them available
ARG MY_BUILD_ARG="default_value"

# Set as ENV so it's available at runtime
ENV MY_BUILD_ARG=${MY_BUILD_ARG}
```

### Secrets (for sensitive values)

For sensitive values like authentication tokens, use `secrets` instead of `buildArgs`. Secrets are mounted during the build but **not persisted in the final image**, making them ideal for:

- AWS CodeArtifact authentication tokens
- Private PyPI registry credentials
- SSH keys for private Git repositories

```typescript title="sst.config.ts"
const withSecrets = new sst.aws.Function("MyPythonFunction", {
python: {
container: true,
secrets: {
// The value is mounted at /run/secrets/<key> during build
CODEARTIFACT_AUTH_TOKEN: process.env.CODEARTIFACT_AUTH_TOKEN,
},
},
handler: "functions/src/functions/api.handler",
runtime: "python3.11",
url: true
});
```

In your Dockerfile, mount the secret using `RUN --mount=type=secret`:

```dockerfile
# Install private packages using secrets (not persisted in image)
RUN --mount=type=secret,id=CODEARTIFACT_AUTH_TOKEN \
CODEARTIFACT_AUTH_TOKEN=$(cat /run/secrets/CODEARTIFACT_AUTH_TOKEN) && \
pip install --extra-index-url \
https://aws:${CODEARTIFACT_AUTH_TOKEN}@my-domain.d.codeartifact.us-east-1.amazonaws.com/pypi/my-repo/simple/ \
my-private-package
```

### Multi-stage Build Targets

Use `target` to stop at a specific stage in a multi-stage Dockerfile:

```typescript
const withTarget = new sst.aws.Function("MyPythonFunction", {
python: {
container: true,
target: "production",
},
handler: "functions/src/functions/api.handler",
runtime: "python3.11",
url: true
});
```

### Network Mode

Set the network mode for `RUN` instructions during the build:

```typescript
const withNetwork = new sst.aws.Function("MyPythonFunction", {
python: {
container: true,
network: "host", // "default" | "host" | "none"
},
handler: "functions/src/functions/api.handler",
runtime: "python3.11",
url: true
});
```

### SSH Forwarding

Mount SSH keys for accessing private Git repositories during the build:

```typescript
const withSSH = new sst.aws.Function("MyPythonFunction", {
python: {
container: true,
ssh: {
default: ["$SSH_AUTH_SOCK"],
},
},
handler: "functions/src/functions/api.handler",
runtime: "python3.11",
url: true
});
```

Live lambda will locally run your python code by building the workspace and running the specified handler. You can have multiple handlers in the same workspace and have multiple workspaces in the same project.

```markdown
Expand Down
34 changes: 34 additions & 0 deletions examples/aws-python-container/build_args/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# The python version to use is supplied as an arg from SST
ARG PYTHON_VERSION=3.11

# Use an official AWS Lambda base image for Python
FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}

# Declare build args AFTER FROM to make them available in the build stage
# This arg will be passed from sst.config.ts via python.buildArgs
ARG MY_BUILD_ARG="default_value"

# Set as ENV so it's available at runtime
ENV MY_BUILD_ARG=${MY_BUILD_ARG}

# Ensure git is installed so we can install git based dependencies (such as sst)
RUN dnf update -y && \
dnf install -y git gcc && \
dnf clean all

# Install UV to manage your python runtime
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

# Install the dependencies to the lambda runtime
COPY requirements.txt ${LAMBDA_TASK_ROOT}/requirements.txt
RUN uv pip install -r requirements.txt --target ${LAMBDA_TASK_ROOT} --system

# Example: Install private packages using secrets (not persisted in image)
# RUN --mount=type=secret,id=CODEARTIFACT_AUTH_TOKEN \
# CODEARTIFACT_AUTH_TOKEN=$(cat /run/secrets/CODEARTIFACT_AUTH_TOKEN) && \
# pip install --extra-index-url https://aws:${CODEARTIFACT_AUTH_TOKEN}@my-domain.d.codeartifact.us-east-1.amazonaws.com/pypi/my-repo/simple/ my-private-package

# Copy the rest of the code
COPY . ${LAMBDA_TASK_ROOT}

# No need to configure the handler or entrypoint - SST will do that
13 changes: 13 additions & 0 deletions examples/aws-python-container/build_args/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[project]
name = "build_args"
version = "0.1.0"
description = "Build Args Example"
dependencies = ["sst"]
requires-python = "==3.11.*"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.uv.sources]
sst = { git = "https://github.com/sst/sst.git", branch = "dev", subdirectory = "sdk/python" }
18 changes: 18 additions & 0 deletions examples/aws-python-container/build_args/src/build_args/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import os
import json


def handler(event, context):
"""
Handler that returns the build argument value passed during Docker build.
This demonstrates that buildArgs are correctly passed to the container build.
"""
build_arg_value = os.environ.get("MY_BUILD_ARG", "NOT_SET")

return {
"statusCode": 200,
"body": json.dumps({
"message": "Hello from build_args example!",
"build_arg_value": build_arg_value,
}),
}
1 change: 1 addition & 0 deletions examples/aws-python-container/build_args/sst.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""This file is generated by SST. Do not edit."""
2 changes: 1 addition & 1 deletion examples/aws-python-container/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies = []
requires-python = "==3.11.*"

[tool.uv.workspace]
members = ["functions", "core", "custom_dockerfile"]
members = ["functions", "core", "custom_dockerfile", "build_args"]

[tool.uv.sources]
sst = { git = "https://github.com/sst/sst.git", subdirectory = "sdk/python", branch = "dev" }
57 changes: 54 additions & 3 deletions examples/aws-python-container/sst.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,54 @@
* # ...
* ```
*
* You can also pass build arguments to the Docker build using `buildArgs`. This is
* useful for passing secrets or configuration values that are needed during the
* Docker build process, such as authentication tokens for private package registries.
*
* ```ts title="sst.config.ts" {3-5}
* const withBuildArgs = new sst.aws.Function("PythonFnBuildArgs", {
* python: {
* container: true,
* buildArgs: {
* MY_BUILD_ARG: "my-value",
* },
* },
* handler: "./build_args/src/build_args/api.handler",
* runtime: "python3.11",
* url: true,
* });
* ```
*
* In your Dockerfile, declare the ARG after the FROM statement to make it available
* in the build stage.
*
* ```dockerfile title="build_args/Dockerfile" {7-11}
* ARG PYTHON_VERSION=3.11
* FROM public.ecr.aws/lambda/python:${PYTHON_VERSION}
*
* # Declare build args AFTER FROM to make them available in the build stage
* ARG MY_BUILD_ARG="default_value"
*
* # Set as ENV so it's available at runtime
* ENV MY_BUILD_ARG=${MY_BUILD_ARG}
* ```
*
* The project structure looks something like this.
*
* ```txt {5}
* ```txt {5,10}
* ├── sst.config.ts
* ├── pyproject.toml
* └── custom_dockerfile
* ├── custom_dockerfile
* │ ├── pyproject.toml
* │ ├── Dockerfile
* │ └── src
* │ └── custom_dockerfile
* │ └── api.py
* └── build_args
* ├── pyproject.toml
* ├── Dockerfile
* └── src
* └── custom_dockerfile
* └── build_args
* └── api.py
* ```
*
Expand Down Expand Up @@ -110,9 +148,22 @@ export default $config({
url: true,
});

const withBuildArgs = new sst.aws.Function("PythonFnBuildArgs", {
python: {
container: true,
buildArgs: {
MY_BUILD_ARG: "hello-from-build-args",
},
},
handler: "./build_args/src/build_args/api.handler",
runtime: "python3.11",
url: true,
});

return {
base: base.url,
custom: custom.url,
withBuildArgs: withBuildArgs.url,
};
},
});
Loading