Skip to content

Storage

Pass a Storage backend to the Agent and every subsystem that needs persistence resolves from it automatically. The SDK handles namespacing so sessions and offloaded content never collide. You can also pass storage directly to individual plugins when different subsystems need different backends.

The SDK ships three backends. Pick one based on where you need your data to live:

BackendWhere data livesBest for
InMemoryStorageProcess memoryTests, short-lived agents
LocalFileStorageLocal filesystemDevelopment, single-machine
S3StorageAmazon S3Production, multi-instance

The simplest approach: pass a single storage backend to the Agent and let subsystems resolve from it.

storage = S3Storage("my-bucket", prefix="agents/prod/")
agent = Agent(
storage=storage,
session_manager=SnapshotSessionManager("my-session"),
context_manager="auto",
)

Both the session manager and context offloader read from the same backend without extra wiring. Each subsystem auto-namespaces its keys (session/ for sessions, offloader/ for offloaded content), so data never collides.

When different subsystems need different backends, pass storage directly to the plugin. This overrides the agent-level default for that plugin only.

agent = Agent(
session_manager=SnapshotSessionManager(
"my-session", storage=S3Storage("my-bucket")
),
plugins=[ContextOffloader(storage=InMemoryStorage())],
)

Storage resolves in this order for each subsystem:

  1. Explicit: storage passed directly to the plugin
  2. Agent-level: the agent’s storagestorage parameter (namespaced automatically)
  3. Fallback: InMemoryStorage for Context Offloader; LocalFileStorage for Session Manager in Python, or an error in TypeScript

Data lives in process memory. No constructor arguments. Fast, zero-config, gone when the process exits.

storage = InMemoryStorage()

Each key becomes a file under a base directory. Writes are atomic (temp file + rename).

ParameterDefaultDescription
base_dirbaseDir"./.strands/"Root directory
sandboxsandboxNone/undefinedOptional Sandbox
storage = LocalFileStorage("./my-data/")

You can also bind a sandbox after construction with for_sandbox(sandbox)forSandbox(sandbox), which returns a new instance routed through the sandbox.

Stores data as objects in an S3 bucket. The AWS SDK loads lazily, so applications that never construct an S3Storage pay nothing.

ParameterDefaultDescription
bucket(required)S3 bucket name
prefix""Key prefix (namespace within the bucket)
region_nameregionNone/undefinedAWS region override
boto_sessions3ClientNone/undefinedPre-configured client
storage = S3Storage("my-bucket", prefix="agents/prod/")

The credentials used by S3Storage need these permissions:

  • s3:PutObject to create and update data
  • s3:GetObject to retrieve data
  • s3:DeleteObject to delete data
  • s3:ListBucket to list keys under the configured prefix

This policy grants the required permissions for one bucket:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-agent-sessions/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-agent-sessions"
}
]
}

You cannot pass both a region and a pre-configured client; pick one or the other.

Implement four async methods (write, read, delete, listwrite, read, delete, list) and pass your class anywhere a Storage is accepted. In Python, Storage is a protocol; in TypeScript, implement the interface.

Community backends can add methods beyond the core four (e.g. search for vector similarity, or structured queries for databases like DynamoDB). Plugins that only need basic persistence use the four standard methods; plugins that need richer access can check for and use the extra surface your backend provides.

When using agent-level storage, each subsystem scopes its keys under its own prefix automatically (session/, offloader/), so you never need to worry about collisions. If you write a custom plugin that consumes agent-level storage, call

storage.namespace('my-prefix/')storage.namespace('my-prefix/')

to claim your own prefix and avoid overlapping with other subsystems.