Deploy OpenClaw on AWS with Amazon Bedrock AgentCore

📅 2026-02-23📖 ~28 min readAgentCoreOpenClawHow-To
OpenClaw is an open-source personal AI agent that connects to messaging platforms like Telegram, Discord, Slack, and WhatsApp. It enables users to interact with foundation models through their preferred communication channels, extend capabilities through a growing ecosystem of over 3,000 community-built skills, and run as a persistent assistant. With skills ranging from AWS infrastructure management to web research and task automation, OpenClaw provides a flexible foundation for building personalized AI assistants.

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.

🚀 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:

  1. A user sends a message on Telegram or Slack. The messaging platform delivers it as an HTTPS webhook to Amazon API Gateway.
  2. 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.
  3. The Router Lambda calls InvokeAgentRuntime with a per-user session ID. AgentCore Runtime either routes the request to an existing microVM for that user or creates a new one.
  4. 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.
  5. 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.
  6. 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:

  1. When a message arrives, the Router Lambda extracts the channel-specific user identifier (for example, Telegram user ID 6087229962 or Slack user ID U05ABC123).
  2. It looks up the CHANNEL#telegram:6087229962 key in DynamoDB to find the associated internal user ID, creating a new one if this is the first message from that user.
  3. It retrieves or creates a session ID for that user (stored under USER#{user_id} / SESSION).
  4. It calls InvokeAgentRuntime with 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/, where namespace is 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:

  1. 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.
  2. 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:

  1. 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.
  2. 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:

json
{
  "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:

  1. entrypoint.sh starts the contract server immediately. AgentCore health-checks the /ping endpoint within seconds of container creation. If this endpoint is not responding, the container is terminated.
  2. The contract server responds to /ping with Healthy right away. No initialization has occurred yet beyond starting the HTTP listener.
  3. On the first /invocations request with action: 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 with USER_ID and CHANNEL environment 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
  4. 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.
  5. 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:

  1. Receiving OpenAI-format requests from OpenClaw
  2. 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
  3. Translating the request to the Bedrock ConverseStream API format
  4. 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:

  1. The eventbridge-cron skill runs inside the user’s AgentCore microVM. When the user requests a scheduled task, the agent invokes the skill’s create_cron_schedule tool, which creates an EventBridge schedule in the openclaw-cron schedule 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 pattern openclaw-{namespace}-{shortId}, and metadata is stored in DynamoDB for listing and management.
  2. 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 (FlexibleTimeWindow is set to OFF). The configurable lead time (default: 5 minutes) in the Cron Lambda accommodates container cold starts by initiating warmup before the scheduled execution.
  3. The Cron executor Lambda processes the scheduled task in three phases. First, it warms up the user’s AgentCore container by sending a warmup action and polling every 15 seconds until the container reports ready (up to 5 minutes). Then it sends the scheduled message to the container as a cron action, 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-Token header against a secret registered with Telegram’s setWebhook API. This header is set by Telegram on every webhook delivery.
  • Slack — The Lambda verifies the X-Slack-Signature HMAC-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

  1. In the AWS Management Console, navigate to Amazon Bedrock > Model access in your target Region.
  2. Request access to Anthropic Claude Opus 4.6 (or the model specified in cdk.json).
  3. 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

bash
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:

json
{
  "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

bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Step 3: Bootstrap CDK (first time only)

bash
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.

bash
# 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

bash
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

bash
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

bash
# 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.

  1. Go to api.slack.com/apps and create a new app from scratch.
  2. Under Features > OAuth & Permissions > Bot Token Scopes, add: chat:write, im:history, im:read, and im:write.
  3. Install the app to your workspace.
  4. Under Features > App Home, enable Messages Tab and check Allow users to send Slash commands and messages from the messages tab.
  5. 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).
  6. Subscribe to the message.im bot event.
  7. Store the credentials in Secrets Manager:
bash
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:

  1. Make your changes to the code in the bridge/ directory.
  2. Increment image_version in cdk.json (this forces AgentCore to pull the new image).
  3. Build and push the updated image to ECR.
  4. 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:

bash
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:

📖 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

💬 Comments

Comments are reviewed before appearing
No comments yet. Be the first to share your thoughts!