Skip to content

On AWS

Deploy the app and gateway Workers on Cloudflare, and create each organization's Computer VM on EC2 yourself

On AWS the gateway runs with COMPUTER_VMS set to self-managed: it creates no VMs and needs no platform Worker. You create one EC2 instance per organization, and the instance connects out to the gateway with that organization’s host token.

The gatewayYou
Shows the Computer as Online while its VM is connected and Offline otherwiseCreate, size, stop, start and delete the VM
Holds a Slack message that arrives while the VM is offline, says so once in the thread, and runs it when the VM connectsStart the VM again

The examples use app.example.com and gateway.example.com, as in On Azure.

1. Get the code

git clone https://github.com/diggerhq/opencomputer.git
cd opencomputer
(cd services/managed-agents && npm ci)
(cd cloudflare-workers/api-edge && npm ci)
(cd web && npm ci)

2. Create the Cloudflare resources and the apps

Follow two steps of the Azure guide unchanged:

3. Deploy the gateway Worker

Follow Deploy the gateway Worker with these differences in wrangler.production.jsonc:

FieldValue
COMPUTER_VMSAdd it, with the value self-managed
servicesRemove the whole block. There is no platform Worker.
COMPUTER_HOST_GATEWAY_URL, COMPUTER_DEFAULT_SIZE, COMPUTER_POOL_SIZERemove. They are not used.

The secrets are the same. Keep the value of COMPUTER_HOST_TOKEN_KEY at hand: step 6 derives each organization’s host token from it.

4. Deploy the app Worker

Follow Deploy the app Worker unchanged.

5. Build and upload the Computer release

cd computer
deploy/bundle.sh /tmp/calvin.bundle

export AWS_REGION=us-east-1
export BUCKET=<globally unique>
aws s3 mb "s3://$BUCKET" --region "$AWS_REGION"
aws s3 cp /tmp/calvin.bundle "s3://$BUCKET/calvin.bundle"

The bundle holds the Computer source and the model proxy built for Linux amd64. The bucket stays private; step 7 gives the VM a URL that is valid for one hour.

6. Get the organization ID and make the host token

  1. Open https://app.example.com and sign in.
  2. Open Settings and copy the Organization ID.
  3. Derive the organization’s host token from COMPUTER_HOST_TOKEN_KEY:
export ORG_ID='<ORGANIZATION_ID>'
export COMPUTER_HOST_TOKEN_KEY='<the gateway secret>'

export TOKEN="sph_$(printf 'calvin-host:%s' "$ORG_ID" \
  | openssl dgst -sha256 -hmac "$COMPUTER_HOST_TOKEN_KEY" -binary \
  | base64 | tr '+/' '-_' | tr -d '=')"

The token is valid for that organization only, and for as long as the key does not change.

7. Create the VM

The VM is Ubuntu 24.04 on amd64 with a second, blank EBS volume for /data. It needs outbound internet access and no inbound access: a new security group has no inbound rules.

Write the first-boot script. It stores the gateway URL and the token in a root-only file, downloads the release and runs its boot script:

BUNDLE_URL="$(aws s3 presign "s3://$BUCKET/calvin.bundle" --expires-in 3600 --region "$AWS_REGION")"

cat > computer-user-data.sh <<EOF
#!/bin/bash
set -euo pipefail
root_disk=\$(lsblk -no PKNAME "\$(findmnt -no SOURCE /)")
data_disk=\$(lsblk -dno NAME,TYPE | awk -v r="\$root_disk" '\$2 == "disk" && \$1 != r { print \$1; exit }')
umask 077
cat > /root/calvin-computer.env <<ENV
SP_GATEWAY_URL=wss://gateway.example.com/v1/computer/host?organizationId=$ORG_ID
SP_HOST_TOKEN=$TOKEN
SP_HOST_ID=$ORG_ID
DATA_DEVICE=/dev/\$data_disk
ENV
umask 022
apt-get update -q
apt-get install -y -q git curl
install -d -m 0755 /opt/bootstrap
curl -fsSL --retry 5 '$BUNDLE_URL' -o /opt/bootstrap/calvin.bundle
rm -rf /opt/bootstrap/calvin-src
git clone -q --branch main /opt/bootstrap/calvin.bundle /opt/bootstrap/calvin-src
BOOTSTRAP_FETCH=0 bash /opt/bootstrap/calvin-src/deploy/vm/azure-computer-boot.sh
EOF
chmod 600 computer-user-data.sh

Create the security group and the instance in the default VPC:

AMI="$(aws ssm get-parameter --region "$AWS_REGION" --query Parameter.Value --output text \
  --name /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id)"

SG="$(aws ec2 create-security-group --region "$AWS_REGION" --group-name computer \
  --description 'Computer VMs: no inbound' --query GroupId --output text)"

aws ec2 run-instances --region "$AWS_REGION" \
  --image-id "$AMI" --instance-type m7i.large \
  --security-group-ids "$SG" \
  --block-device-mappings \
    'DeviceName=/dev/sda1,Ebs={VolumeSize=64,VolumeType=gp3}' \
    'DeviceName=/dev/sdf,Ebs={VolumeSize=128,VolumeType=gp3,DeleteOnTermination=false}' \
  --metadata-options HttpTokens=required \
  --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=computer},{Key=organization,Value=$ORG_ID}]" \
  --user-data file://computer-user-data.sh \
  --query 'Instances[0].InstanceId' --output text

The first boot installs the toolchains, formats the blank volume, mounts it at /data and starts the host. It takes about five minutes. The boot script formats a volume only when it is blank.

The token stays in the instance’s user data. On the VM only root can read it; in AWS, anyone allowed to call ec2:DescribeInstanceAttribute can.

8. Add the Slack app and connect GitHub

  1. Open https://app.example.com. The Computer shows Online once the VM has connected.
  2. Click Add to Slack and approve.
  3. Connect GitHub and choose the repositories.
  4. Mention @computer in a channel.

Repeat steps 6 and 7 for each further organization.

Stop, start and resize

Stop the instance with aws ec2 stop-instances; the Computer shows Offline and Slack messages wait. Start it with aws ec2 start-instances; the host reconnects and the workspace is still on /data. To resize, stop the instance, change its type with aws ec2 modify-instance-attribute, and start it.

Terminating the instance does not delete the data volume.

Check a VM

The VM has no SSH access. The boot log is /var/log/calvin-boot.log, and the two services are slack-presence-host and oc-modelproxy. From a root shell on the VM, for example through AWS Systems Manager Session Manager (the instance then needs an instance profile with the AmazonSSMManagedInstanceCore policy):

systemctl is-active slack-presence-host.service oc-modelproxy.service
tail -50 /var/log/calvin-boot.log

Update a VM

A self-managed VM does not update itself. Build and upload a new bundle (step 5), download it to /opt/bootstrap/calvin.bundle on the VM, and run as root:

git -C /opt/bootstrap/calvin-src fetch -q /opt/bootstrap/calvin.bundle main
git -C /opt/bootstrap/calvin-src checkout -q -f --detach FETCH_HEAD
CODE_ONLY=1 BOOTSTRAP_FETCH=0 bash /opt/bootstrap/calvin-src/deploy/vm/azure-computer-boot.sh