Optimize your agent for Amazon Bedrock AgentCore Runtime V2
Amazon Bedrock AgentCore Runtime V2 starts your agent by restoring a snapshot. This changes how you structure your agent code. Work that your agent does at startup is captured in the snapshot and shared by every restored instance. Produce any value that must differ between requests, or that can expire, in your request handler instead. This topic describes how to structure your agent around startup work and per-request work so that it stays correct after each restore. For an overview of platform version V2, see Platform versions.
Your agent has two execution contexts:
- Startup
-
Code that runs once as your process starts, before AgentCore Runtime takes the snapshot. AgentCore Runtime captures this code in the snapshot, and every instance inherits its results.
- Request handling
-
The code in your
/invocationshandler. This code runs on every request on every instance.
Use one rule to decide where code belongs. Compute a value at startup if it stays the same for the life of the snapshot. Compute it in your handler if it varies per request or can expire. AgentCore Runtime takes one snapshot per agent version and uses it until you create or update the agent, so the life of the snapshot is the life of that version.
Initialize once at startup
Do expensive, reusable work as your process starts, before the snapshot. For example, import dependencies, load model weights, or read static configuration from your deployment bundle. With the AgentCore SDK, do this work at module scope, before you call app.run(). Your agent does not listen on port 8080 until app.run() runs, so no /ping can succeed and no snapshot can be taken until your startup work finishes. The snapshot captures a fully initialized agent by construction, and you do not need to gate /ping. Complete initialization within 120 seconds of startup. If your agent does not become healthy in time, the runtime fails its health check. For more information about the /ping and /invocations endpoints, see Understand the AgentCore Runtime service contract.
import json, pathlib from bedrock_agentcore.runtime import BedrockAgentCoreApp # Runs once at import, before app.run() starts the server and before the # snapshot. Every restored instance inherits these objects. MODEL = load_model_weights() SETTINGS = json.loads((pathlib.Path(__file__).parent / "agent.json").read_text()) app = BedrockAgentCoreApp() @app.entrypoint def invoke(payload): # Per-request work runs here on every instance. ... app.run() # starts listening on 8080; the snapshot is taken after this
If your agent runs its own HTTP server instead of the AgentCore SDK, report a healthy status from /ping only after initialization completes, so that the snapshot captures a fully initialized agent.
Read only data at startup that is the same for every instance and does not expire. Handle time-sensitive values, such as short-lived credentials, in your handler.
Note
Do not compute anything at startup that you change without redeploying. For example, a tool catalog fetched from AgentCore Gateway looks like an ideal startup value because it is slow and expensive to load, but caching it at startup freezes your agent’s tool inventory at the time of the snapshot.
Keep per-request state fresh
The snapshot is captured once and shared by every restored instance, so any value that your agent generates at startup is identical across instances and fixed at the time of the snapshot. Handle values that the snapshot cannot carry in your /invocations handler. The following table describes values to compute in your handler instead of at startup.
| To do this | Do it in the handler | Reason |
|---|---|---|
|
Generate random values, identifiers, or tokens |
Call |
|
|
Read the current time |
Compute it for each request |
A timestamp captured at startup is fixed at the time of the snapshot. |
|
Measure elapsed time |
Take the reference timestamp in your handler |
|
|
Use credentials or tokens |
Refresh them when they expire |
Credentials loaded at startup can expire before an instance starts. |
|
Identify the instance or worker |
Generate an id per request; do not derive it from the host |
Every restored instance reports the same hostname ( |
Build reusable clients at startup, and compute per-request values on each call.
import os, time # Build reusable clients at startup, before the snapshot. Exercise them here too # (for example, with a warm-up call) so the setup the client caches — endpoint and # credential resolution, connection pool — is captured in the snapshot. client = build_client() warm_up(client) @app.entrypoint def invoke(payload): creds = get_credentials() # refreshed when expired, not read at startup request_id = os.urandom(16).hex() # unique per request now = time.time() # current time, not snapshot time # Handle the request.
Use snapshot-safe cryptographic libraries
When AgentCore Runtime restores an instance from a snapshot, a cryptographic library that cached random state at startup can reuse that state across instances. Your cryptographic libraries must use a snapshot-safe (snapsafe) build that reseeds after a restore.
- Direct code deployments
-
The service-managed base image already includes a snapshot-safe build of its cryptographic libraries, so you do not need to take any action for them.
- Bring-your-own cryptographic libraries
-
If you bring your own cryptographic libraries, for example in a container agent, use snapshot-safe builds so that they reseed after a restore. On Amazon Linux 2023, use
openssl-snapsafe-libs.
Networking
- Build clients at startup and expect a transparent reconnect
-
The socket you open at startup does not survive a restore, but the setup your client library caches around it — service-model parsing, endpoint resolution, credential resolution, and the connection pool — does. Construct and exercise your clients at startup, and expect the first call after a restore to re-establish the connection transparently.
- Do not use the hostname or PID as a unique instance identifier
-
Every restored instance starts from the same snapshot and reports the same hostname and process ID. Generate a unique identifier in each request.
- Avoid binding to a fixed source port
-
After a restore, the connection held at snapshot time is re-established, and a fixed source port can collide with that replacement within the instance. Restored instances are separate microVMs with their own network namespaces, so the conflict is within an instance, not across them.