OpenClaw on hungryhub-ai
OpenClaw provisions a CloudFormation stack that boots an Ubuntu 24.04 EC2 instance with openclaw, an AI agent gateway with multi-channel support (Slack, Discord, Telegram, WhatsApp, Feishu). Instances are provider-agnostic: use Amazon Bedrock or any OpenAI-compatible HTTP endpoint (e.g. LiteLLM).
- Repo: hungryhub-team/hungryhub-ai (
openclaw/) - Module:
hungryhub-ai/openclaw/ - Access: SSM Session Manager port-forward (not public)
Architecture
LiteLLM / Bedrock
│
▼
openclaw gateway (EC2 :18789)
│
┌────────────────┼────────────────┐
│ │ │
WhatsApp Discord Telegram Slack
Provider types:
bedrock— Amazon Bedrock models via IAM roleopenai— any OpenAI-compatible HTTP endpoint (LiteLLM, OpenAI, Anthropic, etc.)
Deployment: CloudFormation on AWS EC2 (t4g.medium default), SSM Session Manager for operator access.
Key files
| File | Purpose |
|---|---|
openclaw/main.tf | aws_cloudformation_stack.openclaw (for_each over instance map) |
openclaw/variable.tf | openclaw_instances map (per-instance config) |
openclaw/output.tf | stack/instance IDs, SSM token paths |
openclaw/templates/openclaw.yaml | 720-line CloudFormation (user_data, IAM, SG) |
env/dev.tfvars | Sandbox deployments (currently empty) |
env/prod.tfvars | Production deployments |
.github/workflows/terraform.yml | CI: services: openclaw |
Instance naming
Each entry in openclaw_instances maps short name → config:
| Instance key | Stack name | Auth mode | SSM token path |
|---|---|---|---|
claw-seo | openclaw-claw-seo-prod | none | (not generated) |
claw-marketing | openclaw-claw-marketing-prod | token | /openclaw/openclaw-claw-marketing-prod/gateway-token |
Adding an instance: one map entry in env/<env>.tfvars.
Deployment
Prerequisites
- Shared VPC —
shared/module must be applied first (provides VPC + subnets) - SSM key — For
Provider=openai, create a SecureString in SSM:aws ssm put-parameter \ --name "/openclaw/openclaw-<instance>-<env>/<provider>-api-key" \ --value "<api-key>" \ --type SecureString \ --region us-east-1 \ --profile genai-prod
Apply
# Dev (genai-sandbox)
aws sso login --profile genai-sandbox
cd openclaw
terraform init -backend-config="bucket=hungryhub-ai-tf-state-us-965444437277" \
-backend-config="key=openclaw/terraform.tfstate" \
-backend-config="region=us-east-1" \
-backend-config="dynamodb_table=terraform-state-lock"
terraform plan -var environment=dev -var-file=../env/dev.tfvars -out=tfplan
terraform apply tfplan
# Prod (genai-prod)
aws sso login --profile genai-prod
# ... same, with bucket ending in 512438352490
Validate
INSTANCE_ID=$(aws cloudformation describe-stacks \
--stack-name openclaw-<instance>-<env> \
--query 'Stacks[0].Outputs[?OutputKey==`InstanceId`].OutputValue' \
--output text --region us-east-1 --profile genai-prod)
# Get token (only when auth_mode = "token"; ignored otherwise)
TOKEN=$(aws ssm get-parameter \
--name /openclaw/openclaw-<instance>-<env>/gateway-token \
--with-decryption --query Parameter.Value --output text \
--region us-east-1 --profile genai-prod)
# Start session
aws ssm start-session --target $INSTANCE_ID --region us-east-1 \
--profile genai-prod \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["18789"],"localPortNumber":["18789"]}'
# Or use the wrapper in hh-seo-geo (default local port 28789 to avoid
# clashing with another local OpenClaw tunnel on the upstream default):
# ../hh-seo-geo/bin/openclaw-port-forward.sh # localhost:28789
# ../hh-seo-geo/bin/openclaw-port-forward.sh 29000 # custom port
# Test
# Open http://localhost:18789/ (auth_mode = none)
# Open http://localhost:18789/#token=$TOKEN (auth_mode = token; URL fragment, NOT query string)
# Or CLI:
aws ssm start-session --target $INSTANCE_ID --profile genai-prod
sudo su - ubuntu
openclaw agent -m "reply with just the word OK" --agent main
Instance lifecycle
Add
- Add SSM SecureString for the API key (if
Provider=openai) - Append entry to
openclaw_instancesinenv/<env>.tfvars:openclaw_instances = { "my-instance" = { openclaw_version = "latest" provider = "openai" provider_base_url = "https://litellm.hungryhub.com/v1" model_id = "minimax/MiniMax-M3" api_key_ssm_param = "/openclaw/openclaw-my-instance-prod/litellm-api-key" instance_type = "t4g.medium" enable_sandbox = true enable_monitoring = true auth_mode = "none" # default; set "token" to require a gateway token } } terraform plan -var environment=prod -var-file=../env/prod.tfvars -out=tfplanterraform apply tfplan
Retire
- Remove entry from
openclaw_instancesin tfvars terraform apply(CFN stack deletes EC2)- Delete SSM params manually:
aws ssm delete-parameter --name /openclaw/openclaw-<instance>-<env>/gateway-token --profile genai-prod aws ssm delete-parameter --name /openclaw/openclaw-<instance>-<env>/litellm-api-key --profile genai-prod
Parameters
| Variable | Type | Default | Description |
|---|---|---|---|
openclaw_instances | map(object) | {} | Per-instance config |
openclaw_version | string | "latest" | Pin or track |
provider | string | "bedrock" | "bedrock" or "openai" |
provider_base_url | string | "" | Required for openai provider |
model_id | string | "us.amazon.nova-pro-v1:0" | Bedrock model ID or upstream model name |
api_key_ssm_param | string | "" | SSM SecureString for openai key |
instance_type | string | "t4g.medium" | EC2 instance size |
enable_sandbox | bool | true | Install Docker for plugin sandboxing |
enable_monitoring | bool | true | CloudWatch log group + IAM |
create_vpc_endpoints | bool | false | SSM-only VPC endpoints (rare) |
auth_mode | string | "none" | Gateway auth: "none" (default; safe for loopback-only) or "token" |
Authentication
The gateway binds to 127.0.0.1 (loopback) and is reachable only via SSM
port-forward from your laptop. Anyone with SSM access to the instance
already has root-equivalent privileges, so the gateway-level token was
just adding friction (stale localStorage, manual URL fragments, no
easy rotation) without buying real security on top of the SSM IAM
boundary.
The auth_mode per-instance field controls this:
"none"(default) —openclaw.jsonhasgateway.auth.mode = "none". No token is generated or stored in SSM. The instance writesauth.mode = "none"on first boot. The dashboard is reachable directly viahttp://localhost:18789/(no URL fragment needed)."token"— On first boot the instance generates a random 48-char hex token viaopenssl rand -hex 24, stores it inopenclaw.json(gateway.auth.token), and persists it in SSM at/openclaw/openclaw-<instance>-<env>/gateway-token. The dashboard URL ishttp://localhost:18789/#token=<token>(URL fragment, not query string —?token=works too, but may appear in browser/server logs).
Changing auth_mode after the instance is up does not rewrite
openclaw.json on the existing box (the user_data only runs on first
boot). To switch an instance between none and token, either
terraform apply with a forced instance replacement (e.g. via
in-place=false + new AMI) or patch the live config with SSM and
restart the systemd user service (systemctl --user restart openclaw-gateway).
Why auth_mode = "none" is the default
- The gateway is loopback-only — there is no path from outside the EC2 instance to port 18789 except through SSM.
- SSM port-forward is already gated by
ssm:StartSessionIAM, which is the team’s primary access boundary. - Token auth via URL fragment + browser
localStorageis fragile — copying a token between profiles, switching browsers, or using an incognito window that previously connected to a different instance on the samelocalhost:18789origin can all trigger spurious “Auth did not match” errors with no real security benefit.
Use "token" only when the gateway is reachable from a wider network
(e.g. via Tailscale, ALB, or a non-loopback bind) or when a compliance
requirement demands an application-level auth layer.
Legacy: hungryhub-terraform/lightsail/
A prior OpenClaw deployment lived in hungryhub-terraform/lightsail/
as aws_cloudformation_stack resources targeting EC2 + Bedrock.
Removed in hungryhub-terraform PR #542
(merged 2026-07-07). The directory name was a misnomer — it was
CloudFormation-on-EC2, not AWS Lightsail.
Why removed:
- All five instances (
end-user,vendor,platform,automation,data-internal) had been commented out inlocal.openclaw_instancessincefe78a5f(PR #164, 2026-05-06). - The module was Bedrock-specific and incompatible with the LiteLLM
path that the active
hungryhub-ai/openclaw/deployment uses. - Six orphan
/openclaw/openclaw-*-prod/gateway-tokenSSM parameters in the prod account (ap-southeast-1) — matching the commented-out instance names — were deleted in the same cleanup pass.
That Terraform PR also updated terraform-pr-plan.yml so the
auto-detect step skips modules whose directory no longer exists in the
PR (the guard was previously hard-failing with “Module folder not
found”). State cleanup for the deleted module:
# Verified empty (outputs: {}, no resources) — safe to delete
aws s3 rm s3://hungryhub-prod-terraform-202255947274/lightsail/lightsail-state.tfstate \
--profile prod --region ap-southeast-1
Upstream
Template forked from aws-samples/sample-OpenClaw-on-AWS-with-Bedrock (commit 2026-06). Key upstream improvements cherry-picked:
OpenClawVersionparameterEnableMonitoring(CloudWatch)- IMDSv2 required
OPENCLAW_STATE_DIRenv propagation- systemd memory limits
.envfile for credentials
Costs
| Resource | Est. monthly (USD) |
|---|---|
| EC2 t4g.medium | ~$30 |
| EBS 30GB gp3 | ~$4 |
| CloudWatch logs (14d) | ~$1 |
| LiteLLM inference | Pay-per-use |
Related
- Relay Bot — Architecture and Operations — HungryHub’s Slack AI assistant
- hungryhub-ai README — Terraform module overview
- hungryhub-ai AGENTS.md — Agent conventions
- hh-seo-geo
bin/openclaw-port-forward.sh— local-port wrapper for the dashboard SSM tunnel
Changelog
- 2026-07-07 — Legacy
hungryhub-terraform/lightsail/module removed (PR #542). Six orphangateway-tokenSSM parameters in the prod account cleaned up.hungryhub-terraform/terraform-pr-plan.ymlupdated to skip deleted modules in the auto-detect step. Added a stablebin/openclaw-port-forward.shwrapper in hh-seo-geo PR #1 (default local port28789to avoid the upstream18789clash). - 2026-06-24 — Added
auth_modeper-instance field (defaultnone). Disables token-based gateway auth, which was just adding friction without security benefit on a loopback-only, SSM-port-forwarded gateway. The two SSM/token-related CFN Outputs (Step3GetToken,GatewayTokenSsmPath) are gated byIsTokenAuthso they don’t appear when auth is disabled. See hungryhub-ai PR #31 (new commit onfeat/hai-claw-seo). - 2026-06-23 — Module added to hungryhub-ai. Sandbox validation of
claw-seoon genai-sandbox (MiniMax M3 via LiteLLM) completed successfully, then intentionally torn down to save cost. Production deployment on genai-prod (claw-seo-prod, instancei-06a521617f8b1bf9d) is live and validated —openclaw agent -m 'reply with the word PROD-OK' --agent mainreturnsPROD-OKwithprovider=openai, model=minimax/MiniMax-M3. See hungryhub-ai PR #31 and knowledge-base PR #284.