OCTOBER 24, 2026

Deploying a Strands agent to Amazon Bedrock AgentCore Runtime

Strands Agents | AgentCore Runtime | Amazon ECR | Python

DEPLOYMENT MAPThe AWS resources and roles
DEPLOYMENT IDENTITYAWS Identity and Access Management iconDeployer IAM principalECR push · AgentCore control · IAM PassRole
CONTAINER REGISTRYAmazon Elastic Container Registry iconAmazon ECRhello-strands image digest
COMPUTEAmazon Bedrock AgentCore iconAgentCore RuntimeRuns the container · port 8080
ASSUMED ROLEAWS Identity and Access Management iconRuntime execution roleTrusted by bedrock-agentcore.amazonaws.com
MODELAmazon Bedrock iconAmazon BedrockInvoke model
OBSERVABILITYAmazon CloudWatch iconCloudWatch LogsWrite runtime logs
IMAGE PULLAmazon Elastic Container Registry iconAmazon ECRRead image layers
01

FROM SCRIPT TO RUNTIME

Put the local Strands agent behind an AgentCore entrypoint

The local starter already knows how to create a Strands agent, connect it to an Amazon Bedrock model, and register a custom tool. Deployment adds one important boundary: instead of calling the agent directly from a script, AgentCore invokes a stable application entrypoint with a request payload.

This project keeps the agent intentionally small. A caller sends a prompt, the runtime builds the Bedrock-backed agent, and the response returns the agent’s message. The simplicity makes the deployment path easy to see.

The complete implementation is available in p-cap/fluffy-enigma on GitHub ↗.
02

AGENTCORE APPLICATION

Turn a Python function into the invocation endpoint

BedrockAgentCoreApp owns the HTTP runtime contract. The @app.entrypoint decorator marks the function AgentCore calls for each invocation, while the Strands @tool decorator exposes deterministic application logic to the model.

Py Pythonapp/main.py
app = BedrockAgentCoreApp()

@tool
def count_words(text: str) -> int:
    """Count whitespace-separated words in the provided text."""
    return len(text.split())

@app.entrypoint
def test_strands(payload):
    load_dotenv()
    user_message = payload.get("prompt", "Hello")

    model = BedrockModel(
        model_id=os.environ["BEDROCK_MODEL_ID"],
        region_name=os.environ["AWS_REGION"],
    )
    agent = Agent(
        model=model,
        system_prompt=(
            "You are a helpful assistant. "
            "Use the count_words tool when asked to count words."
        ),
        tools=[count_words],
    )
    result = agent(user_message)
    return result.message

if __name__ == "__main__":
    app.run()

The runtime package serves the AgentCore HTTP API on port 8080. Locally, a request to /invocations exercises the same entrypoint that runs after deployment.

03

CONTAINER BOUNDARY

Build once for the AgentCore runtime architecture

The image starts from the Python 3.13 uv base, installs the locked dependencies, copies only the application directory, and starts the runtime without resolving packages again.

# DockerfileDockerfile
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim

WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev

COPY app/ ./app/
EXPOSE 8080

CMD ["uv", "run", "--no-sync", "python", "app/main.py"]

The repository builds for linux/arm64, matching the runtime target rather than the developer machine.

$ Terminallocal image
docker buildx build   --platform linux/arm64   --tag hello-strands:local   --load .
04

TEST THE IMAGE

Pass temporary AWS credentials into the local container

The container needs the same AWS identity, Region, and model configuration as the local script. AWS CLI can export the active session as environment variables, while the non-secret application settings come from .env.

$ Terminalrun locally
credentials=$(aws configure export-credentials --format env) || exit 1
eval "$credentials"

docker run --rm   --name hello-strands-local   --platform linux/arm64   -p 127.0.0.1:8080:8080   --env-file .env   -e AWS_ACCESS_KEY_ID   -e AWS_SECRET_ACCESS_KEY   -e AWS_SESSION_TOKEN   hello-strands:local

This keeps credentials out of the image and source tree. They exist only in the environment of the local container process.

05

ECR DELIVERY

Push the image, then deploy its immutable digest

The deployment shell script reads configuration from .env, checks that Docker is available, builds the image when it is missing, resolves the current AWS account, and creates the ECR repository if necessary. It then authenticates Docker, tags the local image, and pushes it.

./push-agentcore-runtime.sh

Although the pushed tag defaults to latest, the Python deployer resolves that tag to its ECR digest and sends the digest URI to AgentCore. Every runtime version therefore points to the exact image that was pushed.

Py Pythonscripts/deploy_runtime.py
image = ecr.describe_images(
    repositoryName=repository,
    imageIds=[{"imageTag": os.environ["IMAGE_TAG"]}],
)["imageDetails"][0]

uri = (
    f"{account}.dkr.ecr.{region}.amazonaws.com/"
    f"{repository}@{image['imageDigest']}"
)
06

CREATE OR REDEPLOY

Make the deployment safe to run more than once

The deployer searches every page of AgentCore runtimes for the configured name. If none exists, it creates one. If it finds exactly one, it updates that runtime while preserving its execution role, networking, authorization, request headers, lifecycle settings, and other existing configuration.

That distinction matters: a deployment script should not silently replace security or networking choices on every release. It should update the artifact and required environment while retaining intentional runtime settings.

Create when missing. Update when present. Refuse to guess when duplicate names exist.

The script also waits through CREATING or UPDATING until the selected version reaches READY. Failure states include the AgentCore failure reason, and the wait stops after fifteen minutes.

07

RUNTIME EXECUTION ROLE

Give AgentCore only the services the container needs

When a role ARN is not supplied, the deployer reuses or creates a role trusted by bedrock-agentcore.amazonaws.com. Its inline policy covers four responsibilities: authorize with ECR, pull this repository’s image layers, write AgentCore CloudWatch logs, and invoke Bedrock models or inference profiles.

New IAM roles can take a moment to propagate. The creation path retries the specific role-assumption validation error instead of treating every exception as temporary. If an existing RUNTIME_ROLE_ARN is supplied, the script uses it without rewriting its permissions.

08

DEPLOYMENT TESTS

Test the decisions around the AWS calls

The repository’s unit tests mock the AWS clients and verify the behaviors that are easiest to break: digest-based creation, configuration-preserving updates, pagination, access-denied handling, failure reporting, and execution-role setup.

These tests do not claim that AWS is available. They protect the deployment logic so a real AWS run is reserved for validating integration, permissions, and runtime readiness.

uv run python -m unittest scripts/test_deploy_runtime.py
09

SOURCE MATERIAL

Read the implementation