Production-ready Terraform configuration for deploying OpenClaw on AWS EC2 with HTTPS/SSL, automated backups, and enterprise security. One-command deployment with automatic Let's Encrypt SSL certificates.
# Clone repository
git clone https://github.com/qodex-ai/openclaw-worker.git
cd openclaw-worker
# Setup AWS credentials
cp .aws.env.example .aws.env
nano .aws.env # Add your AWS access key and secret key
source .aws.env
# Configure Terraform
cp terraform.tfvars.example terraform.tfvars
nano terraform.tfvars # Add your IP, domain, email, and Anthropic API key
# Deploy
terraform init
terraform apply # Type 'yes' when prompted
# Wait 8-10 minutes for bootstrap + SSL certificate, then get HTTPS dashboard URL
terraform output -raw dashboard_url_with_token
# Pair your browser (first-time only)
$(terraform output -raw ssh_command)
openclaw devices list # See pending pairing request
openclaw devices approve <request-id>Open the HTTPS URL in your browser. Done! β
For detailed instructions, see SETUP_GUIDE.md.
After successful deployment, create a LOCAL_README.md file (git-ignored) to store your actual configuration:
- EC2 instance details, SSH keys, IP addresses
- Dashboard URLs with tokens
- S3 bucket names
- Quick reference commands with your real values
See the template at the end of this README.
This Terraform configuration automatically deploys a production-ready OpenClaw instance on AWS with enterprise-grade security and automation:
- EC2 Instance (t3.medium) β 4GB RAM, Ubuntu 24.04 LTS, auto-updates enabled
- Security Group β Locked down to your IP (SSH), HTTPS/HTTP open for SSL validation
- S3 Bucket β Encrypted backups with automatic cleanup (180 days retention)
- IAM Role & Instance Profile β Secure EC2-to-S3 access without hardcoded credentials
- Elastic IP β Static public IP that persists across restarts
- Route53 DNS β A record pointing your domain to the Elastic IP
- SSH Key Pair β Auto-generated 4096-bit RSA key
- SSM Integration β AWS Systems Manager for secure access
- Node.js 22 β Latest LTS version
- OpenClaw β Installed via npm for easy updates
- Docker β Required for OpenClaw's container management
- Nginx β HTTPS reverse proxy with SSL termination
- Let's Encrypt SSL β Free, auto-renewing SSL certificates via Certbot
- Systemd Service β Auto-start on boot with automatic restarts
- Management CLI β Custom
occommand for operations
- User Data Script β Automated installation and configuration
- SSL Certificate β Automatic acquisition and renewal via Let's Encrypt
- DNS Management β Route53 A record automatically created
- Automated Daily Backups β Cron job runs daily at 2 AM UTC
- Manual Backups β On-demand backup to S3 via
oc backupcommand - S3 Lifecycle Policy β Automatic deletion after 180 days
- GitHub Actions β CI/CD with Terraform validation and security scanning
Note: Daily backups run automatically. Additional backups can be configured through OpenClaw's interface if needed.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ARCHITECTURE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β YOUR DOMAIN βββββββΊ Route53 DNS β
β (HTTPS/SSL) β β
β βΌ β
β EC2 (t3.medium) β
β β β
β Let's Encrypt βββββΊ Nginx (HTTPS) βββΊ OpenClaw (:18789) β
β (SSL Cert) β β β
β β βββ Node.js 22 β
β YOUR IP ββββββββββΊ SSH (Port 22) βββ Docker β
β βββ Slack API β
β β β
β βΌ β
β S3 Bucket β
β (encrypted backups) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Resource | Monthly Cost |
|---|---|
| EC2 t3.medium | ~$30 |
| EBS 20GB | ~$2 |
| S3 backups | ~$1 |
| Total | ~$33/month |
- AWS Account with IAM user access keys
- Domain Name with Route53 hosting (e.g.,
openclaw.yourdomain.com) - Email Address for SSL certificate notifications
- Terraform (v1.0+)
- AWS CLI installed
- Anthropic API Key (
sk-ant-...)
Note: This setup uses environment variables (.aws.env file) for AWS credentials instead of aws configure. This keeps credentials project-local and git-ignored. HTTPS with valid SSL certificate is required for browser security. Slack integration is configured through OpenClaw's interface after deployment.
git clone https://github.com/qodex-ai/openclaw-worker.git
cd openclaw-workercp .aws.env.example .aws.env
nano .aws.envAdd your AWS credentials:
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=us-east-1Load credentials:
source .aws.env
aws sts get-caller-identity # Verify they workcurl -4 ifconfig.me # Returns your IPv4 addresscp terraform.tfvars.example terraform.tfvars
nano terraform.tfvarsEdit with your values:
aws_region = "us-east-1"
instance_type = "t3.medium"
# Your IP (add /32 at the end)
my_ip_cidrs = ["49.47.128.13/32"]
# Your domain name (must be in Route53)
domain_name = "openclaw.yourdomain.com"
# Your email for SSL certificate notifications
email = "you@example.com"
# Your Anthropic API key
anthropic_api_key = "sk-ant-api03-xxxxx"
# Optional: Route53 hosted zone ID (auto-detected if not provided)
route53_zone_id = "Z1234567890ABC"terraform init
terraform applyType yes when prompted. Wait ~3 minutes for AWS resources.
The EC2 instance will auto-install OpenClaw, nginx, and obtain SSL certificate (8-10 minutes). Then:
terraform output -raw dashboard_url_with_tokenOpen the HTTPS URL in your browser β done! π
Note: The first time you access the dashboard, you'll need to approve device pairing via SSH (see Device Pairing section). Daily S3 backups run automatically at 2 AM UTC. Configure Slack integration through OpenClaw's dashboard after deployment.
| File/Directory | Description |
|---|---|
main.tf |
Core AWS infrastructure (EC2, S3, IAM, Security Groups, Elastic IP) |
variables.tf |
Input variable definitions and validation |
outputs.tf |
Output values (IPs, URLs, SSH commands, tokens) |
user_data.sh |
EC2 bootstrap script - installs Node.js, Docker, and OpenClaw via npm |
terraform.tfvars.example |
Template for Terraform variables (copy to terraform.tfvars) |
.aws.env.example |
Template for AWS credentials (copy to .aws.env) |
SETUP_GUIDE.md |
Complete step-by-step deployment guide (~45 minutes) |
CRON_BACKUPS.md |
Automated backup configuration and management guide |
.github/workflows/ci.yml |
Automated Terraform validation and security scanning |
.gitignore |
Git ignore rules for Terraform and sensitive files |
LICENSE |
MIT License (2025 Qodex AI) |
- HTTPS/SSL β Valid Let's Encrypt certificates with auto-renewal
- IP Restriction β Only your IP can access SSH (22)
- Device Pairing β Secure multi-device authentication system
- IMDSv2 β Instance metadata service v2 required
- Encrypted EBS β Root volume encrypted at rest
- Encrypted S3 β AES-256 server-side encryption
- No Public S3 β Bucket blocks all public access
- IAM Role β EC2 uses role-based access (no hardcoded credentials)
- Token Auth β 48-character random gateway token
SSH into your instance:
$(terraform output -raw ssh_command)Then use the oc command:
| Command | Description |
|---|---|
oc status |
Check if OpenClaw is running |
oc logs |
View logs (follow mode) |
oc restart |
Restart OpenClaw |
oc stop |
Stop OpenClaw |
oc start |
Start OpenClaw |
oc backup |
Manual backup to S3 (on-demand) |
oc restore <file> |
Restore from S3 backup |
oc update |
Update OpenClaw to latest |
oc url |
Show dashboard URL with token |
oc token |
Show gateway token |
Automated Backups: Daily backups run automatically at 2 AM UTC via cron. View logs: tail -f ~/.openclaw/backup.log
OpenClaw uses device pairing for secure access. When you first access the dashboard, you'll need to pair your browser.
- Access the dashboard - Open the dashboard URL in your browser
- SSH to server:
$(terraform output -raw ssh_command) - List pending pairing requests:
openclaw devices list
- Approve the request:
openclaw devices approve <request-id>
- Refresh the dashboard - Your browser is now paired!
openclaw devices list # List pending and paired devices
openclaw devices approve # Approve a pending device
openclaw devices reject # Reject a pending device
openclaw devices revoke # Revoke a paired deviceIf you prefer to skip device pairing and use only the gateway token:
openclaw config set gateway.auth.mode token
sudo systemctl restart openclawTo revert to pairing mode:
openclaw config unset gateway.auth.mode
sudo systemctl restart openclawIf your IP changes:
# Edit terraform.tfvars with new IP
nano terraform.tfvars
# Apply changes (instant, no restart)
terraform apply# SSH into server
$(terraform output -raw ssh_command)
# Update
oc update# List all backups in S3
aws s3 ls s3://$(terraform output -raw s3_bucket)/backups/
# View automated backup logs
ssh -i openclaw-key.pem ubuntu@<your-ip>
tail -f ~/.openclaw/backup.log# SSH into server
$(terraform output -raw ssh_command)
# View cron schedule
crontab -l
# Check recent automated backups
tail -20 ~/.openclaw/backup.log# SSH into server
$(terraform output -raw ssh_command)
# List backups
oc restore
# Restore specific backup
oc restore openclaw-backup-20240115.tar.gzAfter deployment, these outputs are available:
terraform output # All outputs
terraform output instance_public_ip # Server IP
terraform output ssh_command # SSH command
terraform output -raw dashboard_url_with_token # Dashboard URL
terraform output -raw gateway_token # Just the token
terraform output s3_bucket # Backup bucket nameTo destroy all resources:
# Download backups first (optional)
aws s3 sync s3://$(terraform output -raw s3_bucket) ./my-backups/
# Destroy everything
terraform destroy| Variable | Default | Description |
|---|---|---|
aws_region |
us-east-1 |
AWS region |
instance_type |
t3.medium |
EC2 instance type |
my_ip_cidrs |
β | Your IP(s) for SSH access (required) |
domain_name |
β | Domain name for HTTPS (required) |
email |
β | Email for SSL notifications (required) |
anthropic_api_key |
β | Anthropic API key (required) |
route53_zone_id |
(auto) | Route53 zone ID (optional) |
instance_type = "t3.small" # $15/month, 2GB RAM
instance_type = "t3.medium" # $30/month, 4GB RAM (default)
instance_type = "t3.large" # $60/month, 8GB RAMmy_ip_cidrs = [
"203.0.113.50/32", # Home
"198.51.100.25/32", # Office
]aws_region = "eu-west-1" # Ireland
aws_region = "ap-south-1" # MumbaiIf you get a timeout error with AWS provider on macOS:
# Remove Gatekeeper quarantine from Terraform providers
xattr -r -d com.apple.quarantine .terraform/providers/
# Then retry
terraform planThis happens because macOS Gatekeeper blocks unsigned provider binaries.
# Test credentials
source .aws.env
aws sts get-caller-identity
# If it fails, verify the .env file has 'export' statements:
cat .aws.env # Should show: export AWS_ACCESS_KEY_ID=...- Check your IP hasn't changed:
curl -4 ifconfig.me - Update
terraform.tfvarswith new IP - Run
terraform apply
# SSH into server
$(terraform output -raw ssh_command)
# Check service status
oc status
# View logs
oc logs
# Check bootstrap log
sudo cat /var/log/openclaw-bootstrap.logVerify IAM role:
# SSH to server
$(terraform output -raw ssh_command)
# Test S3 access
aws s3 ls s3://$(terraform output -raw s3_bucket)/
# If it fails, check IAM role attachment
aws sts get-caller-identityThe EC2 instance has an IAM role automatically configured by Terraform. If backups fail:
- Verify the role is attached to the instance
- Check CloudWatch logs for permissions errors
- Ensure the S3 bucket exists:
terraform output s3_bucket
Test backup:
ssh -i openclaw-key.pem ubuntu@<your-ip>
oc backupExpected output: Backup uploaded: s3://your-bucket/backups/openclaw-backup-YYYYMMDD-HHMMSS.tar.gz
Fixed in latest version. If you encounter this with an older version:
Problem: Ubuntu 24.04 doesn't have awscli in default apt repositories.
Solution: The user_data.sh script now installs AWS CLI v2 directly from Amazon. Update to the latest version:
git pull origin main
terraform init -upgrade
terraform applyIf you need to fix a running instance manually:
ssh -i openclaw-key.pem ubuntu@<your-ip>
cd /tmp
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
sudo apt-get install -y unzip
unzip -q awscliv2.zip
sudo ./aws/install
rm -rf aws awscliv2.zip
aws --versionProblem: Terraform stuck for hours during apply on Apple Silicon Macs.
Root Cause: Intel x86_64 Terraform running under Rosetta causes AWS provider crashes.
Solution: Use ARM64 native Terraform:
# Uninstall Intel version
brew uninstall terraform
# Download ARM64 version
cd ~/Downloads
wget https://releases.hashicorp.com/terraform/1.14.4/terraform_1.14.4_darwin_arm64.zip
unzip terraform_1.14.4_darwin_arm64.zip
# Install to ~/bin
mkdir -p ~/bin
mv terraform ~/bin/
chmod +x ~/bin/terraform
# Add to PATH (add this to ~/.zshrc for persistence)
export PATH="$HOME/bin:$PATH"
# Verify
terraform --version
file ~/bin/terraform # Should show: Mach-O 64-bit executable arm64
# Reinitialize to get ARM64 providers
cd ~/Projects/flinket/jarvis/openclaw-worker
rm -rf .terraform
terraform init
terraform apply- This Repository: qodex-ai/openclaw-worker
- OpenClaw npm package
- OpenClaw GitHub
- Terraform AWS Provider
- AWS EC2 Pricing
- Complete Setup Guide - Step-by-step instructions
- Automated Backup Guide - Cron backup configuration and management
After deployment, create a LOCAL_README.md file (automatically git-ignored) to store your actual configuration values:
# Create your local documentation
nano LOCAL_README.mdWhat to include:
- EC2 instance ID, public IP, SSH key path
- Dashboard URL with token
- S3 bucket name
- Actual Terraform commands with your values
- AWS credentials reference
- Quick reference commands for daily use
Example structure:
# My OpenClaw Deployment
## Instance Info
- Instance ID: i-xxxxx
- Public IP: x.x.x.x
- Domain: openclaw.yourdomain.com
- Dashboard (HTTPS): https://openclaw.yourdomain.com/?token=xxxxx
- Dashboard (Direct): http://x.x.x.x:18789/?token=xxxxx
## SSL Certificate
- Provider: Let's Encrypt
- Expires: [Date]
- Auto-renewal: Enabled
## Quick Commands
ssh -i openclaw-key.pem ubuntu@x.x.x.x
terraform output -raw dashboard_url_with_token
aws s3 ls s3://my-bucket/backups/
openclaw devices list
## Credentials
- AWS Account: 123456789012
- S3 Bucket: my-openclaw-backups-xxx
- Anthropic Key: sk-ant-xxx...This keeps your actual values separate from version control while maintaining easy access to deployment details.
Contributions welcome! Please:
- Fork the repo
- Create a feature branch
- Make your changes
- Submit a pull request
MIT License β feel free to use, modify, and distribute.
If this helped you, please star the repo!
Made with β€οΈ for the OpenClaw community