Skip to content

Deploy on an AWS EC2 instance

The quickest way to try InterLock on AWS: one EC2 instance runs the same Docker Compose stack as the quick start, including the sample shop database. Nothing is exposed to the internet. Every port listens on the instance’s 127.0.0.1 only, its security group admits SSH from your address alone, and you reach the console and the gateway through an SSH tunnel.

Plan on about fifteen minutes, most of it the instance building the images. A t3.medium (2 vCPUs, 4 GiB) builds and runs the whole stack. Terminate it when you are done.

  • An AWS account and the AWS CLI v2, signed in (aws sts get-caller-identity answers).
  • An SSH key pair on your machine, for example ~/.ssh/id_ed25519.pub.
  • psql, if you want to follow the quick start’s PostgreSQL steps.

Set the region and a name, and find the address your connections come from:

Terminal window
export AWS_REGION=us-east-1
export NAME=interlock-eval
export MY_CIDR="$(curl -4 -fsS https://checkip.amazonaws.com)/32"

The instance goes in the region’s default VPC. Some accounts have none; this creates one only if it is missing:

Terminal window
export VPC_ID="$(aws ec2 describe-vpcs --filters Name=is-default,Values=true \
--query 'Vpcs[0].VpcId' --output text)"
if [ "$VPC_ID" = "None" ]; then
export VPC_ID="$(aws ec2 create-default-vpc --query 'Vpc.VpcId' --output text)"
fi

The instance configures itself on first boot from this cloud-init file. It installs Docker from Ubuntu’s packages, checks out an InterLock release, generates a private signing key for console sessions, and starts the stack:

Terminal window
cat > interlock-cloud-init.yaml <<'EOF'
#cloud-config
package_update: true
packages: [docker.io, docker-compose-v2, git, openssl]
runcmd:
- git clone --depth 1 --branch v1.0.0-rc.15 https://github.com/ContextData/interlock /opt/interlock
- bash -c 'cd /opt/interlock && umask 077 && printf "INTERLOCK_ADMIN__SECRET_KEY=%s\n" "$(openssl rand -hex 32)" > .env'
- bash -c 'cd /opt/interlock && docker compose --profile quickstart up -d --wait > /var/log/interlock-up.log 2>&1'
EOF

To install a different release, change v1.0.0-rc.15 to its tag.

Terminal window
export SG_ID="$(aws ec2 create-security-group --group-name "$NAME" \
--description "InterLock evaluation: SSH from one address" --vpc-id "$VPC_ID" \
--query GroupId --output text)"
aws ec2 authorize-security-group-ingress --group-id "$SG_ID" \
--protocol tcp --port 22 --cidr "$MY_CIDR" > /dev/null
aws ec2 import-key-pair --key-name "$NAME" \
--public-key-material fileb://"$HOME/.ssh/id_ed25519.pub" > /dev/null

If your address changes, add the new one with the same authorize-security-group-ingress command.

This starts Ubuntu 24.04 (the image ID comes from Canonical’s public parameter, so it is always the current one), with a root volume large enough to build the images and the instance metadata service limited to session tokens. The tag is set in a variable first: written inline inside $( ), bash would brace-expand it:

Terminal window
TAGS="ResourceType=instance,Tags=[{Key=Name,Value=$NAME}]"
export INSTANCE_ID="$(aws ec2 run-instances \
--image-id resolve:ssm:/aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id \
--instance-type t3.medium --key-name "$NAME" --security-group-ids "$SG_ID" \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":40,"VolumeType":"gp3","Encrypted":true}}]' \
--metadata-options HttpTokens=required \
--user-data file://interlock-cloud-init.yaml \
--tag-specifications "$TAGS" \
--query 'Instances[0].InstanceId' --output text)"
aws ec2 wait instance-running --instance-ids "$INSTANCE_ID"
export INSTANCE_IP="$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \
--query 'Reservations[0].Instances[0].PublicIpAddress' --output text)"

The first boot installs Docker, then builds the images. This waits for all of it, then lists the services. SSH can take a minute to come up after the instance starts; if the first command is refused, run it again:

Terminal window
ssh ubuntu@"$INSTANCE_IP" cloud-init status --wait
ssh ubuntu@"$INSTANCE_IP" 'cd /opt/interlock && sudo docker compose --profile quickstart ps'

Every service should be running, and the gateway, admin, PostgreSQL, Redis and sample database healthy. If something failed, the output of the start-up is in /var/log/interlock-up.log on the instance, and sudo docker compose logs <service> in /opt/interlock shows one service’s logs.

This forwards the console, the gateway and the gateway’s PostgreSQL port to the same addresses on your machine, and runs until you stop it with Ctrl-C:

Terminal window
ssh -N -L 9090:127.0.0.1:9090 -L 3001:127.0.0.1:3001 -L 5434:127.0.0.1:5434 ubuntu@"$INSTANCE_IP"
On your machine Is
http://127.0.0.1:9090 Admin console
http://127.0.0.1:3001 Gateway, HTTP and MCP
127.0.0.1:5434 Gateway, PostgreSQL wire

These are the addresses the quick start uses, so from here on it works unchanged: continue with step 2 of the quick start, signing in as admin with the password admin and choosing a new one. Run its curl and psql commands on your machine, in another terminal, while the tunnel is open.

Terminating the instance deletes its root volume:

Terminal window
aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" > /dev/null
aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID"
aws ec2 delete-security-group --group-id "$SG_ID"
aws ec2 delete-key-pair --key-name "$NAME" > /dev/null

If you created the default VPC for this, it costs nothing to keep; to remove it, delete its internet gateway, subnets and the VPC itself in the VPC console.