Deploy OpenClaw on AWS with Amazon Bedrock AgentCore
- Solution overview
- Per-user session isolation
- Workspace persistence with Amazon S3
- Webhook ingestion and async dispatch
- Multimodal image input
- Container startup and lazy initialization
- Scheduled tasks with Amazon EventBridge
- Security controls
- Prerequisites
- Deploy the solution
- Add Slack as a second channel
- Extending OpenClaw with skills
- Deploy updates
- Clean up
- Conclusion
Running OpenClaw on AWS gives you access to Amazon Bedrock‘s unified API for foundation models, including Anthropic Claude, Amazon Nova, Meta Llama, and DeepSeek, without managing individual provider API keys. However, deploying a multi-user AI agent introduces challenges around session isolation, state persistence, cost management, and security that go beyond running a single chatbot instance.
Amazon Bedrock AgentCore Runtime addresses these challenges by running each user’s agent in a dedicated serverless microVM with isolated CPU, memory, and file system. When the session ends, the microVM is terminated and all state is cleaned up. This isolation model prevents data leakage between users without requiring you to manage container orchestration or implement application-level tenant separation.
In this post, we walk through how to deploy OpenClaw on AgentCore Runtime as a multi-user, multi-channel AI assistant. We cover:
- How the per-user session architecture works and why it provides stronger isolation than shared-instance deployments
- How workspace persistence to Amazon Simple Storage Service (Amazon S3) enables continuous user experiences across ephemeral sessions
- How the Router Lambda handles webhook ingestion from Telegram and Slack with cryptographic request validation
- How users can send images for multimodal understanding via the Amazon Bedrock ConverseStream API
- How scheduled tasks use Amazon EventBridge Scheduler for recurring jobs with response delivery to the user’s messaging channel
- How to deploy the full solution using the AWS Cloud Development Kit (AWS CDK)
- Security controls layered across the network, identity, data, and application layers
The complete source code is available in the sample-host-openclaw-on-amazon-bedrock-agentcore repository on GitHub.
Solution overview
The solution consists of seven CDK stacks that deploy a fully serverless, multi-channel AI messaging system. The following diagram illustrates the high-level architecture.
Telegram webhook / Slack Events API
(text, photos, file attachments)
|
+-----------v-----------+
| API Gateway HTTP | Explicit routes only, rate limiting (burst 50, rate 100)
| POST /webhook/* | POST /webhook/telegram, POST /webhook/slack, GET /health
+-----------+-----------+
|
+-----------v-----------+
| Router Lambda | Async self-invoke for background processing
| - Webhook validation| Telegram: X-Telegram-Bot-Api-Secret-Token
| - User resolution | Slack: X-Slack-Signature HMAC-SHA256
| - Image upload to S3| JPEG, PNG, GIF, WebP (max 3.75 MB)
| - Session mgmt | DynamoDB identity table
| - Channel dispatch | Cross-channel account binding
+-----------+-----------+
|
+-----------v-----------+
| InvokeAgentRuntime | Per-user session (runtimeSessionId)
| (session per user) |
+-----------+-----------+
|
+-----------v-----------+
| AgentCore Runtime | Per-user microVM (ARM64, VPC mode)
| |
| agentcore-contract.js (8080) -- /ping, /invocations
| -> lazy init on first chat:
| 1. Restore .openclaw/ from S3
| 2. Start Bedrock proxy (18790)
| 3. Start OpenClaw headless (18789)
| 4. Bridge messages via WebSocket
| -> SIGTERM: save .openclaw/ to S3
| |
| agentcore-proxy.js (18790) -- OpenAI -> Bedrock ConverseStream
| Multimodal: text + images from S3
| OpenClaw Gateway (18789) -- headless, skills: s3-user-files,
| eventbridge-cron
+-----------+-----------+
|
+-----------v-----------+
| Amazon Bedrock |
| ConverseStream API |
| Claude Opus 4.6 |
| (text + images) |
+-----------------------+
+--------------------+ +---------------------+ +--------------------+
| S3 User Files | | S3 Workspace Sync | | S3 Image Uploads |
| {namespace}/ | | {namespace}/ | | {namespace}/ |
| file.md | | .openclaw/ | | _uploads/img_* |
| Via s3-user-files | | Restored on init, | | From Telegram/ |
| skill | | saved periodically | | Slack photos |
+--------------------+ +---------------------+ +--------------------+
+-------------------------------------------------------------+
| EventBridge Scheduler --> Cron Lambda --> AgentCore |
| (openclaw-cron group) warmup, execute, deliver response |
| Per-user schedules to user's Telegram/Slack channel |
+-------------------------------------------------------------+
Supporting: VPC, KMS, Secrets Manager, Cognito,
CloudWatch, DynamoDB, CloudTrail
The preceding figure illustrates the end-to-end message flow:
- A user sends a message on Telegram or Slack. The messaging platform delivers it as an HTTPS webhook to Amazon API Gateway.
- API Gateway routes the request to the Router AWS Lambda function, which validates the webhook signature, resolves the user’s identity in Amazon DynamoDB, downloads and stages any attached images to Amazon S3, and self-invokes asynchronously to process the request in the background while returning an immediate 200 response to the webhook.
- The Router Lambda calls
InvokeAgentRuntimewith a per-user session ID. AgentCore Runtime either routes the request to an existing microVM for that user or creates a new one. - Inside the microVM, the contract server triggers lazy initialization on the first request: restoring the user’s workspace from S3, starting the Bedrock proxy, and launching OpenClaw in headless mode.
- The contract server bridges the message to OpenClaw via WebSocket. OpenClaw processes the message using its tools and skills, with the Bedrock proxy translating requests to the Amazon Bedrock ConverseStream API. When the message includes images, the proxy fetches the staged files from S3 and includes them as multimodal content blocks in the Bedrock request.
- The streaming response is collected and returned to the Router Lambda, which sends it back to the user through the originating channel’s API.
The architecture uses seven CDK stacks:
| Stack | Key resources |
|---|---|
| OpenClawVpc | VPC with two Availability Zones, private subnets, NAT gateway, and seven VPC endpoints for private connectivity to AWS services |
| OpenClawSecurity | AWS Key Management Service (AWS KMS) customer managed key, AWS Secrets Manager for bot tokens and credentials, Amazon Cognito User Pool for identity, and AWS CloudTrail for audit logging |
| OpenClawAgentCore | AgentCore Runtime, Runtime Endpoint, WorkloadIdentity, Amazon Elastic Container Registry (Amazon ECR) repository, S3 bucket, and IAM roles |
| OpenClawRouter | Router Lambda, API Gateway HTTP API with explicit routes and throttling, DynamoDB identity table |
| OpenClawObservability | Amazon CloudWatch operations dashboard, alarms for error rates and latency, and Bedrock invocation logging |
| OpenClawTokenMonitoring | DynamoDB table for per-user token usage tracking (single-table design with four GSIs), Lambda log processor, and token analytics dashboard |
| OpenClawCron | Amazon EventBridge Scheduler group for per-user cron jobs, Cron executor Lambda for task warmup and channel delivery, and IAM roles for schedule management |
Per-user session isolation
A critical design decision in any multi-user AI agent deployment is how to isolate users from each other. When multiple users share a single agent process, conversation context, tool outputs, and file system state can leak between sessions. This is particularly problematic for AI agents because the model’s context window accumulates information from the current session, and tools may write to shared directories.
AgentCore Runtime solves this with hardware-level isolation. Each user gets a dedicated microVM with its own CPU, memory, and file system. No two users ever share a process, memory space, or file system. When the session terminates, the microVM is destroyed and all in-memory state is wiped.
The solution implements per-user sessions through the Router Lambda and DynamoDB identity table:
- When a message arrives, the Router Lambda extracts the channel-specific user identifier (for example, Telegram user ID
6087229962or Slack user IDU05ABC123). - It looks up the
CHANNEL#telegram:6087229962key in DynamoDB to find the associated internal user ID, creating a new one if this is the first message from that user. - It retrieves or creates a session ID for that user (stored under
USER#{user_id} / SESSION). - It calls
InvokeAgentRuntimewith this session ID. AgentCore routes the request to the existing microVM for that session, or creates a new one if the session has expired.
This approach means the system can support any number of concurrent users, each with full isolation, without managing container orchestration or implementing application-level tenant separation.
Allowing idle termination for cost efficiency
A key design choice in this solution is allowing AgentCore to terminate idle sessions rather than keeping them alive. The contract server’s health check endpoint returns Healthy (not HealthyBusy), which tells AgentCore that the container can be reclaimed after the configured idle timeout (default: 30 minutes).
This is important for cost efficiency. In a multi-user deployment, most users are active intermittently. If every user’s microVM ran continuously, the compute costs would scale linearly with total users rather than concurrent users. By allowing idle termination:
- You pay only for active sessions — A user who sends a few messages in the morning and a few in the evening does not consume compute during the hours in between.
- The system scales naturally — During peak hours, more microVMs run concurrently. During off-hours, idle sessions terminate and resources are freed.
- No keepalive infrastructure is needed — There is no Lambda function pinging the runtime on a schedule, no EventBridge rules, and no synthetic traffic, which reduces both cost and operational complexity.
The trade-off is that a returning user’s first message after an idle period triggers a cold start (approximately 4 minutes for OpenClaw initialization). The solution handles this gracefully: the contract server returns an “I’m starting up” message immediately while initialization proceeds in the background, and subsequent messages are processed normally.
Workspace persistence with Amazon S3
AgentCore Runtime provides an ephemeral file system. When a session terminates, everything on disk is lost. For a stateless API handler, this is fine. For an AI assistant, it presents a challenge: OpenClaw stores its entire working state in the .openclaw/ directory on the local file system. This includes:
- Conversation memory (
MEMORY.md) — Notes and context the agent has built up across interactions - User preferences (
USER.md) — The user’s communication style, timezone, and language preferences - Agent personality (
SOUL.md,IDENTITY.md) — The agent’s persona, name, and behavioral instructions that the user may have customized - Operating instructions (
AGENTS.md) — Task-specific instructions and workflow definitions - Configuration (
openclaw.json) — Model settings, tool permissions, and skill configuration
Without persistence, every session termination would reset the agent to a blank slate. The user would lose all conversation history, and the agent would forget any customizations or context it had accumulated.
The solution addresses this with S3-backed workspace sync, implemented in workspace-sync.js:
- On session start — Before starting OpenClaw, the contract server downloads the user’s
.openclaw/directory from S3 under the prefix{namespace}/.openclaw/, wherenamespaceis derived from the user’s channel identity (for example,telegram_6087229962). This restores the agent’s full state as if the session had never ended. - Periodically (every 5 minutes) — A background timer uploads changed files to S3, protecting against unexpected failures mid-session.
- On session shutdown (SIGTERM) — AgentCore sends SIGTERM before terminating the microVM, giving a 15-second grace period. The contract server uses up to 10 seconds of this window for a final workspace save.
Build artifacts (node_modules/, .cache/), log files, and files larger than 10 MB are excluded from sync to keep storage costs low (typically under $0.03 per user per month) and restore times fast. Each user’s workspace is stored under a separate S3 prefix, so no cross-user data bleed is possible even at the storage layer.
The Bedrock proxy (agentcore-proxy.js) also reads workspace files at request time and injects them into the system prompt sent to Amazon Bedrock. This means the model has access to the user’s personality settings, operating instructions, and memory notes on every request, enabling personalized responses even in a freshly created session.
Webhook ingestion and async dispatch
AgentCore Runtime is invoked through the InvokeAgentRuntime API, not through direct HTTP connections from messaging platforms. This requires a webhook ingestion layer that receives messages from Telegram and Slack, resolves user identity, and invokes the appropriate AgentCore session.
The Router Lambda and API Gateway HTTP API serve this role. The design addresses a specific constraint: messaging platforms require webhook endpoints to respond quickly (Telegram within 30 seconds, Slack within 3 seconds), but AgentCore invocations can take much longer, particularly during cold starts.
The solution uses async self-invocation:
- The Lambda receives the webhook request, validates the cryptographic signature, and immediately self-invokes with
InvocationType=Event(asynchronous). It returns HTTP 200 to the webhook within milliseconds. - The async invocation runs in the background: it resolves the user in DynamoDB, gets or creates a session, invokes AgentCore, waits for the response, and sends it back to the channel through the Telegram or Slack API.
This pattern decouples webhook response time from agent processing time. The user sees the message as delivered, and the response arrives when the agent finishes processing, which may be seconds (warm session) or minutes (cold start). For Telegram, the Lambda sends a “typing” indicator while the agent processes the request.
Cross-channel account binding
By default, each messaging channel creates a separate user identity. A user who messages from both Telegram and Slack will have two separate sessions with independent conversation histories and workspaces. The solution includes a cross-channel binding mechanism that allows users to unify their identities:
- On the first channel (for example, Telegram), the user sends
link. The Router Lambda generates a 6-character alphanumeric code, stores it in DynamoDB with a 10-minute TTL, and replies with the code. - On the second channel (for example, Slack), the user sends
link A1B2C3. The Lambda redeems the code, links the Slack channel identity to the existing user record, and deletes the code.
After linking, both channels route to the same user ID, the same AgentCore session, and the same S3 workspace. This enables continuous conversation across platforms: start a discussion on Telegram from your phone, continue it on Slack from your desktop, with full context preserved.
Multimodal image input
An AI assistant limited to text misses important context. Users often want to share screenshots, photos of documents, diagrams, or error messages that are easier to show than describe. The solution supports multimodal image input from both Telegram and Slack, enabling the agent to see and reason about images using Amazon Bedrock’s ConverseStream API.
The image pipeline spans three layers:
Platform detection and download
The Router Lambda detects image attachments in incoming webhook payloads. For Telegram, this includes photo messages (the Lambda selects the highest-resolution version from the array) and document messages with an image MIME type, with accompanying text extracted from the message caption field rather than the text field. For Slack, the Lambda detects files with image MIME types attached to messages, including the file_share subtype. In both cases, the Lambda downloads the image binary from the platform’s file API using the bot token.
S3 staging and structured messages
Downloaded images are validated against an allowlist of content types (image/jpeg, image/png, image/gif, image/webp) and a 3.75 MB size limit that matches the Amazon Bedrock API constraint. Valid images are uploaded to the S3 user-files bucket under the path {namespace}/_uploads/img_{timestamp}_{hex}.{ext}, where namespace is derived from the user’s channel identity (for example, telegram_12345). The Router Lambda then sends a structured message to AgentCore containing both the text and an array of S3 image references:
{
"text": "What does this diagram show?",
"images": [
{
"s3Key": "telegram_12345/_uploads/img_1740000000_a1b2c3d4.jpeg",
"contentType": "image/jpeg"
}
]
}
Multimodal content assembly
Inside the microVM, the contract server converts the structured message into a text payload with an appended image marker. When the Bedrock proxy (agentcore-proxy.js) processes the outgoing model request, it extracts these markers using a regex pattern, fetches the image bytes from S3, and assembles a multimodal content array for the Bedrock ConverseStream API. Each image is validated again at this layer: the S3 key must start with the user’s namespace followed by /_uploads/, path traversal patterns (..) are rejected, the content type must be in the allowlist, and the file size must not exceed 3.75 MB. This defense-in-depth approach means that even if a malicious payload bypasses the Router Lambda’s validation, the proxy rejects it before it reaches the model.
The resulting Bedrock request contains both text and image content blocks, enabling the model to reason about visual information alongside the user’s question. This works with any model that supports multimodal input through the Bedrock ConverseStream API, including Anthropic Claude Opus 4.6.
Container startup and lazy initialization
The bridge container runs three co-located processes: the AgentCore contract server (port 8080), the Bedrock proxy (port 18790), and the OpenClaw gateway (port 18789). The container uses lazy initialization to handle the tension between AgentCore’s fast health-check requirement and OpenClaw’s slow startup.
The startup sequence works as follows:
entrypoint.shstarts the contract server immediately. AgentCore health-checks the/pingendpoint within seconds of container creation. If this endpoint is not responding, the container is terminated.- The contract server responds to
/pingwithHealthyright away. No initialization has occurred yet beyond starting the HTTP listener. - On the first
/invocationsrequest withaction: chat, the contract server triggers lazy initialization: – Fetches secrets from Secrets Manager (gateway token, Cognito password secret) – Restores the user’s.openclaw/workspace from S3 – Starts the Bedrock proxy withUSER_IDandCHANNELenvironment variables – Writes the OpenClaw configuration for headless mode (no channel connections — messages are bridged via WebSocket) – Starts the OpenClaw gateway (approximately 4 minutes for plugin registration) – Begins periodic workspace saves - While initialization is in progress, the contract server returns a message telling the user the agent is starting up. This prevents the AgentCore invocation from timing out.
- On subsequent invocations, the contract server bridges messages to OpenClaw through a WebSocket connection, collects streaming response deltas, and returns the accumulated text.
This design lets the container pass health checks immediately while deferring the expensive initialization until a user actually sends a message.
The Bedrock proxy
OpenClaw uses OpenAI-compatible chat completion endpoints as its model interface. Amazon Bedrock uses a different API (ConverseStream). The Bedrock proxy (agentcore-proxy.js, port 18790) bridges this gap by:
- Receiving OpenAI-format requests from OpenClaw
- Constructing a system prompt that includes the user’s workspace files (
AGENTS.md,SOUL.md,USER.md,IDENTITY.md,MEMORY.md), each capped at 4,096 characters - Translating the request to the Bedrock ConverseStream API format
- Streaming the response back in OpenAI-compatible format
The proxy also handles per-user authentication via Amazon Cognito. It derives deterministic passwords using HMAC-SHA256(secret, actorId), auto-provisions users via AdminCreateUser, and caches JWT tokens with early refresh. This provides identity-aware API access without maintaining a password database.
Scheduled tasks with Amazon EventBridge
A persistent assistant should be able to do things on a schedule without waiting for the user to send a message. The solution implements scheduled tasks through a custom OpenClaw skill backed by Amazon EventBridge Scheduler, enabling users to create, update, and delete recurring jobs using natural language.
For example, a user can say “remind me every Monday at 9 AM Sydney time to review my weekly goals” and the agent creates an EventBridge schedule that fires at the specified time, runs the task in the user’s AgentCore session, and delivers the response to their messaging channel.
Architecture
The scheduled task system has three layers:
- The
eventbridge-cronskill runs inside the user’s AgentCore microVM. When the user requests a scheduled task, the agent invokes the skill’screate_cron_scheduletool, which creates an EventBridge schedule in theopenclaw-cronschedule group. The schedule includes the cron or rate expression, timezone, and a payload containing the user’s identity and channel information. Schedules are named with the patternopenclaw-{namespace}-{shortId}, and metadata is stored in DynamoDB for listing and management. - Amazon EventBridge Scheduler fires at the specified time and invokes the Cron executor Lambda with the schedule payload. The schedule fires at the exact specified time (
FlexibleTimeWindowis set toOFF). The configurable lead time (default: 5 minutes) in the Cron Lambda accommodates container cold starts by initiating warmup before the scheduled execution. - The Cron executor Lambda processes the scheduled task in three phases. First, it warms up the user’s AgentCore container by sending a
warmupaction and polling every 15 seconds until the container reports ready (up to 5 minutes). Then it sends the scheduled message to the container as acronaction, prefixed with the schedule name for context (for example,[Scheduled task: weekly-review]). Finally, it delivers the agent’s response to the user’s original messaging channel (Telegram or Slack) using the channel API.
The system supports both cron expressions (cron(0 9 ? * MON *)) and rate expressions (rate(1 hour)). Users can also update, disable, re-enable, or delete schedules through the skill’s additional tools (update_cron_schedule, delete_cron_schedule, list_cron_schedules).
Per-user isolation
All schedules are scoped to the individual user through namespace-prefixed schedule names and DynamoDB partition keys (USER#{namespace} / CRON#{shortId}). The AgentCore execution role’s IAM permissions restrict schedule operations to the openclaw-cron/* schedule group, and each user can only list and manage their own schedules. A configurable lead time (default: 5 minutes, set via cron_lead_time_minutes in cdk.json) ensures the Cron Lambda starts warming up the container before the scheduled execution time, reducing the chance of delayed delivery.
Security controls
The solution applies defense-in-depth across multiple layers.
Network isolation
AgentCore containers run in private VPC subnets with no direct internet exposure. All AWS service access (Bedrock, S3, Secrets Manager, ECR, CloudWatch, DynamoDB) goes through VPC endpoints. The only public entry point is the API Gateway HTTP API, which exposes only three explicit routes (POST /webhook/telegram, POST /webhook/slack, GET /health). All other paths return 404 from API Gateway itself without invoking the Lambda.
Webhook authentication
Every incoming webhook request is cryptographically validated before processing:
- Telegram — The Router Lambda checks the
X-Telegram-Bot-Api-Secret-Tokenheader against a secret registered with Telegram’ssetWebhookAPI. This header is set by Telegram on every webhook delivery. - Slack — The Lambda verifies the
X-Slack-SignatureHMAC-SHA256 header using the Slack app’s signing secret, with a 5-minute timestamp check to prevent replay attacks.
Validation is fail-closed: if the webhook secret or signing secret is not configured, all requests are rejected.
Secret management
All sensitive values (bot tokens, webhook secrets, Cognito password secret, gateway token) are stored in Secrets Manager encrypted with a customer-managed KMS key. Secrets are fetched at runtime and held in process memory only. They are never written to environment variables, configuration files, or logs. The Lambda and container environment variables contain only secret *IDs* (ARN references), not the secret values themselves.
Least-privilege IAM
Each component has tightly scoped permissions:
- The Router Lambda can invoke only the specific AgentCore Runtime ARN, not
Resource: *. - Cognito operations are scoped to the specific User Pool.
- Secrets Manager access is limited to the
openclaw/*prefix. - S3 access is scoped to the specific user-files bucket.
Token monitoring and cost controls
Bedrock invocation logs flow through CloudWatch to a Lambda processor that extracts token counts, estimates costs using model-specific pricing, and writes to a DynamoDB table with four Global Secondary Indexes for different query patterns (per-user, per-channel, per-model, daily cost ranking). CloudWatch alarms fire when daily token usage or estimated cost exceeds configurable thresholds (defaults: 1,000,000 tokens and $5 USD per day).
Automated compliance
Every cdk synth runs cdk-nag AwsSolutions checks against the entire infrastructure, catching common security misconfigurations before deployment.
Prerequisites
Before deploying, verify the following:
- An AWS account with Amazon Bedrock model access enabled for Anthropic Claude Opus 4.6 (or your chosen model)
- AWS CLI v2 configured with valid credentials
- Python 3.11 or later and Node.js 18 or later installed
- Docker with ARM64 build support (Docker Desktop or buildx)
- AWS CDK v2 installed (
npm install -g aws-cdk) - A Telegram bot token from @BotFather (or tokens for your preferred messaging platforms)
Enable Bedrock model access
- In the AWS Management Console, navigate to Amazon Bedrock > Model access in your target Region.
- Request access to Anthropic Claude Opus 4.6 (or the model specified in
cdk.json). - If using cross-Region inference profiles (for example,
global.anthropic.claude-opus-4-6-v1), enable access in all Regions the profile may route to.
Deploy the solution
Step 1: Clone and configure
git clone https://github.com/aws-samples/sample-host-openclaw-on-amazon-bedrock-agentcore.git
cd sample-host-openclaw-on-amazon-bedrock-agentcore
# Set your AWS account and Region
export CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
export CDK_DEFAULT_REGION=us-west-2
Optionally, edit cdk.json to customize the configuration:
{
"context": {
"account": "123456789012",
"region": "us-west-2",
"default_model_id": "global.anthropic.claude-opus-4-6-v1",
"session_idle_timeout": 1800,
"daily_cost_budget_usd": 5
}
}
Key parameters include:
| Parameter | Default | Description |
|---|---|---|
default_model_id |
global.anthropic.claude-opus-4-6-v1 |
Bedrock model ID. The global. prefix enables cross-Region inference routing |
session_idle_timeout |
1800 |
Seconds of idle time before a per-user session terminates (30 minutes) |
session_max_lifetime |
28800 |
Maximum session lifetime in seconds (8 hours) |
daily_token_budget |
1000000 |
Daily token usage alarm threshold |
daily_cost_budget_usd |
5 |
Daily cost alarm threshold in USD |
workspace_sync_interval_seconds |
300 |
How often workspace files are saved to S3 (5 minutes) |
image_version |
1 |
Container image version tag. Increment to force a new container image pull |
cron_lead_time_minutes |
5 |
Minutes before scheduled time to start container warmup |
cron_lambda_timeout_seconds |
600 |
Maximum execution time for the cron executor Lambda (10 minutes) |
Step 2: Install CDK dependencies
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Step 3: Bootstrap CDK (first time only)
cdk bootstrap aws://$CDK_DEFAULT_ACCOUNT/$CDK_DEFAULT_REGION
Step 4: Build and push the container image
The bridge container image must exist in Amazon ECR before the AgentCore stack deploys. Build for ARM64, which is required by AgentCore Runtime.
# Create ECR repository (will be managed by CDK after first deploy)
aws ecr create-repository \
--repository-name openclaw-bridge \
--region $CDK_DEFAULT_REGION 2>/dev/null || true
# Authenticate Docker to ECR
aws ecr get-login-password --region $CDK_DEFAULT_REGION | \
docker login --username AWS --password-stdin \
$CDK_DEFAULT_ACCOUNT.dkr.ecr.$CDK_DEFAULT_REGION.amazonaws.com
# Build ARM64 image
docker build --platform linux/arm64 -t openclaw-bridge bridge/
# Tag and push
docker tag openclaw-bridge:latest \
$CDK_DEFAULT_ACCOUNT.dkr.ecr.$CDK_DEFAULT_REGION.amazonaws.com/openclaw-bridge:latest
docker push \
$CDK_DEFAULT_ACCOUNT.dkr.ecr.$CDK_DEFAULT_REGION.amazonaws.com/openclaw-bridge:latest
Step 5: Deploy all stacks
cdk synth # Validates templates and runs cdk-nag security checks
cdk deploy --all --require-approval never
This deploys seven stacks in dependency order: VPC, Security, AgentCore, Router, Observability, TokenMonitoring, and Cron.
Step 6: Store your bot token
aws secretsmanager update-secret \
--secret-id openclaw/channels/telegram \
--secret-string 'YOUR_TELEGRAM_BOT_TOKEN' \
--region $CDK_DEFAULT_REGION
Step 7: Set up the Telegram webhook
# Get the Router API Gateway URL from stack outputs
API_URL=$(aws cloudformation describe-stacks \
--stack-name OpenClawRouter \
--query "Stacks[0].Outputs[?OutputKey=='ApiUrl'].OutputValue" \
--output text --region $CDK_DEFAULT_REGION)
# Get the webhook secret for request validation
WEBHOOK_SECRET=$(aws secretsmanager get-secret-value \
--secret-id openclaw/webhook-secret \
--region $CDK_DEFAULT_REGION --query SecretString --output text)
# Register the webhook with Telegram, including the secret_token for validation
TELEGRAM_TOKEN=$(aws secretsmanager get-secret-value \
--secret-id openclaw/channels/telegram \
--region $CDK_DEFAULT_REGION --query SecretString --output text)
curl "https://api.telegram.org/bot${TELEGRAM_TOKEN}/setWebhook?url=${API_URL}webhook/telegram&secret_token=${WEBHOOK_SECRET}"
The secret_token parameter tells Telegram to include an X-Telegram-Bot-Api-Secret-Token header on every webhook delivery. The Router Lambda validates this header and rejects requests without a valid token.
Step 8: Verify
Send a message to your Telegram bot. The first message triggers a cold start (approximately 4 minutes for OpenClaw initialization). You will receive an “I’m starting up” response. Send a second message after a few minutes, and the agent will respond with its full capabilities.
Add Slack as a second channel
The solution supports Slack alongside Telegram through the same Router Lambda. Setting up Slack involves creating a Slack app, configuring webhook event subscriptions, and storing credentials in Secrets Manager.
- Go to api.slack.com/apps and create a new app from scratch.
- Under Features > OAuth & Permissions > Bot Token Scopes, add:
chat:write,im:history,im:read, andim:write. - Install the app to your workspace.
- Under Features > App Home, enable Messages Tab and check Allow users to send Slash commands and messages from the messages tab.
- Under Features > Event Subscriptions, enable events and set the Request URL to
${API_URL}webhook/slack(Slack sends a verification challenge and should show a green checkmark). - Subscribe to the
message.imbot event. - Store the credentials in Secrets Manager:
aws secretsmanager update-secret \
--secret-id openclaw/channels/slack \
--secret-string '{"botToken":"xoxb-YOUR-BOT-TOKEN","signingSecret":"YOUR-SIGNING-SECRET"}' \
--region $CDK_DEFAULT_REGION
The signing secret is found under Settings > Basic Information > App Credentials. The Router Lambda uses it to verify the HMAC-SHA256 signature on every incoming Slack webhook request.
To unify your Telegram and Slack identities into a single agent session, use the cross-channel binding feature: send link on Telegram to get a 6-character code, then send link on Slack. Both channels will then route to the same user, session, and conversation history.
Extending OpenClaw with skills
The bridge container comes with ten pre-installed ClawHub skills plus two custom skills for S3-backed file storage and EventBridge-based cron scheduling:
| Skill | Purpose |
|---|---|
duckduckgo-search |
Web search without API key |
jina-reader |
Web content extraction as markdown |
telegram-compose |
Rich HTML formatting for Telegram messages |
transcript |
YouTube transcript extraction |
deep-research-pro |
Multi-step research agent |
hackernews |
Browse and search Hacker News |
news-feed |
RSS-based news aggregation |
task-decomposer |
Break complex requests into subtasks |
cron-mastery |
Cron scheduling and management |
s3-user-files |
Per-user file storage backed by S3 (custom) |
eventbridge-cron |
Schedule recurring tasks via Amazon EventBridge Scheduler (custom) |
The s3-user-files skill is particularly relevant for the serverless deployment. Because the container’s file system is ephemeral, any files the agent creates (reports, code outputs, saved documents) would be lost on session termination. This skill provides durable per-user file storage by reading and writing to Amazon S3, with each user’s files isolated under a separate namespace derived from their channel identity. Files persist across sessions and are subject to configurable S3 lifecycle expiration (default: 365 days).
The eventbridge-cron skill enables the agent to create, update, and delete EventBridge schedules on behalf of the user. When a user requests a recurring task, the agent translates their natural-language request into a cron or rate expression and registers it with EventBridge Scheduler. Schedule metadata is stored in DynamoDB so the user can list and manage their schedules through conversation. See the Scheduled tasks with Amazon EventBridge section for the full architecture.
OpenClaw also runs with its full built-in tool profile enabled, providing access to tool groups for web access, file system operations, runtime commands, session management, and automation workflows.
Skill security considerations
Skills are community-contributed and can contain arbitrary code that runs with the permissions of the host environment. Before installing additional skills, review the source code and verify what AWS APIs or system resources the skill accesses. Scope the IAM role to the minimum permissions needed. Skills that only query data (monitoring dashboards, security scanners) carry less risk than skills that modify infrastructure.
Deploy updates
To deploy a new version of the bridge container:
- Make your changes to the code in the
bridge/directory. - Increment
image_versionincdk.json(this forces AgentCore to pull the new image). - Build and push the updated image to ECR.
- Run
cdk deploy OpenClawAgentCore --require-approval never.
Existing user sessions continue running the old image until they idle-terminate. New sessions (including returning users whose sessions have expired) automatically pick up the new image. No manual session restarts are required.
Clean up
To avoid ongoing charges, delete all resources:
cdk destroy --all
KMS keys and the Cognito User Pool have RETAIN removal policies and are not deleted automatically. Remove them manually from the AWS Management Console if needed.
Conclusion
In this post, we demonstrated how to deploy OpenClaw as a multi-user, multi-channel AI assistant on Amazon Bedrock AgentCore Runtime. The solution provides per-user microVM isolation, S3-backed workspace persistence, webhook-based ingestion from Telegram and Slack, multimodal image understanding, and EventBridge-powered scheduled tasks, all deployed through seven CDK stacks.
Key takeaways:
- Per-user session isolation prevents data leakage — Each user runs in a dedicated AgentCore microVM with separate CPU, memory, and file system. When the session terminates, all in-memory state is destroyed. This is stronger than application-level tenant separation.
- Workspace sync enables continuous user experiences — S3-backed persistence of the
.openclaw/directory lets users maintain conversation history, preferences, and agent customizations across ephemeral sessions, turning a stateless serverless platform into a persistent assistant. - Idle termination reduces costs without degrading experience — By allowing AgentCore to terminate idle sessions (rather than keeping them alive with synthetic traffic), the solution pays only for active users. The lazy initialization pattern handles cold starts gracefully.
- Async webhook dispatch decouples response time from processing time — The Router Lambda’s self-invocation pattern lets the solution meet platform webhook timeout requirements while supporting agent processing times of minutes.
- Defense-in-depth security requires multiple layers — VPC isolation, webhook HMAC validation, Secrets Manager with KMS encryption, least-privilege IAM, per-user S3 namespaces, and automated cdk-nag compliance checks work together to protect the system. No single control is sufficient on its own.
- Multimodal image input extends the assistant beyond text — Images flow from messaging platforms through S3 to the Bedrock ConverseStream API with defense-in-depth validation at the Router Lambda, contract server, and proxy layers. Users can share photos, screenshots, and diagrams without additional configuration.
- Scheduled tasks turn a reactive chatbot into a proactive assistant — The EventBridge Scheduler integration lets users create recurring jobs through natural language. The Cron Lambda handles container warmup, task execution, and response delivery to the user’s channel, bringing time-based automation to a serverless agent deployment.
Get started by cloning the sample-host-openclaw-on-amazon-bedrock-agentcore repository and following the deployment steps in this post. For more information, refer to the following resources:
- Amazon Bedrock AgentCore documentation
- Amazon Bedrock documentation
- AWS CDK documentation
- OpenClaw on GitHub
- OpenClaw ClawHub — Skill Directory
📖 Looking for EC2 or Fargate deployment? If you prefer a traditional instance-based approach, see OpenClaw 深度技术分析 which covers EC2 and Fargate deployment methods, security best practices, and a complete cost analysis. The source code is available in the sample-deploy-openclaw-to-aws repository.
Authors: Melanie Li, Sam Zhang
Special thanks: Alick Wang for technical discussion
Related Posts
- AgentCore Gateway Deep Dive
- AgentCore Memory: Isolation Patterns
- Running Claude Agent SDK Inside OpenClaw on AgentCore
- OpenFang on AWS