Pull request previews on AWS
Run each pull request's stagelet on its own EC2 instance in your AWS account. See what stagely setup creates, the access key it needs and how ECR sign-in works.
Stagely gives each GitHub pull request a stagelet: a private, running copy of your app. On AWS, each stagelet runs on its own EC2 instance in your account. You run stagely setup aws once. After that, the customer agent launches an instance when a pull request deploys, and terminates it when the stagelet ends.
What setup creates
Setup applies a Terraform module with OpenTofu or Terraform. It creates these resources in the region that you choose:
| Resource | Details |
|---|---|
| VPC | 10.42.0.0/16, with an internet gateway and one subnet, 10.42.0.0/20. Instances in the subnet get public addresses, so they need no NAT gateway. |
| Agent host | EC2 instance of type t3.small, from the newest Ubuntu 24.04 AMI of Canonical |
| Gateway host | EC2 instance of type t3.micro, from the same AMI, with an Elastic IP |
| Security groups | One for each host, and one for stagelet machines and build workers |
| EC2 key pair | The SSH key that setup uses to install the agent and the gateway |
| ECR repository creation template | Lets build workers create repositories under the prefix in -registry-prefix when they first push. Setup creates no repository and no other registry. |
Both hosts require IMDSv2. A later run of setup keeps the hosts when Canonical publishes a newer AMI.
Credentials
Setup needs an access key of an IAM user, in AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. The agent keeps the same key on the agent host, in /etc/stagely/stagely-agent.env, which only root can read.
Use a long-lived access key. Do not use temporary credentials with AWS_SESSION_TOKEN:
- Only the long-lived key of an IAM user can call STS
GetFederationToken. The agent needs this call to sign machines in to ECR, so with temporary credentials the first build fails. - Temporary credentials also expire. After that, the agent stops working.
The agent uses these permissions: ec2:DescribeInstances, ec2:RunInstances, ec2:CreateTags, ec2:TerminateInstances, ec2:DescribeKeyPairs, ec2:ImportKeyPair, sts:GetFederationToken and ecr:GetAuthorizationToken. It also needs the ECR push and pull actions and ecr:CreateRepository on the repositories under the prefix.
Stagelet machines and build workers
- The agent launches one instance for each stagelet. Its type is
t3.medium, with a 30 GiB root volume for images, the build cache and stagelet data. Setup has no flag to change this type. - The agent keeps two build workers, also of type
t3.medium. They stay when stagelets come and go. - Each instance starts from the Ubuntu 24.04 AMI, and cloud-init installs Docker at the first boot. With
-golden-image, instances start from an AMI that already has Docker. Thestagelyrepository has the Packer template for this AMI. - Each instance launches into the environment’s subnet. The agent connects to it on its private address over SSH, and accepts only the host key that it gave the instance.
Registry
Build workers push images to Amazon ECR in your account, under <account>.dkr.ecr.<region>.amazonaws.com/<prefix>. The prefix is the environment’s name, unless you set -registry-prefix.
Before each build and each deploy, the agent gets a fresh ECR token for the machine in two steps:
- It calls STS
GetFederationTokenfor temporary credentials. Their policy allows only the repositories under the prefix. A build worker’s credentials can push and pull. A stagelet machine’s credentials can only pull. - It calls ECR
GetAuthorizationTokenwith these temporary credentials. The token works only with the registry, and it expires after 12 hours.
Neither the agent’s access key nor the temporary credentials go to a machine.
Isolation
- The security group of stagelet machines and build workers accepts connections only from the agent’s and the gateway’s security groups. It has no rule for its own members, so a stagelet does not accept connections from another stagelet.
- The agent host accepts only SSH, and only from your
-operator-cidrranges. - The gateway host accepts ports 80 and 443 from anywhere, and SSH from your ranges.
- On each instance, containers cannot open new connections to the instance metadata service at
169.254.169.254. Each instance also requires IMDSv2 with a hop limit of 1, so a container gets no metadata token.
Run setup
export AWS_ACCESS_KEY_ID=<access key id>
export AWS_SECRET_ACCESS_KEY=<secret access key>
stagely login
stagely setup aws \
-environment production \
-region eu-central-1 \
-stagelet-domain stagelets.example.com \
-operator-cidr 203.0.113.7/32 \
-binaries bin/linux-amd64 \
-acme-email ops@example.com
| Flag | Meaning |
|---|---|
-region |
AWS region, required |
-availability-zone |
Availability zone of the environment’s subnet. By default, AWS chooses. |
-registry-prefix |
ECR repository name prefix for built images. The default is the environment’s name. |
-golden-image |
AMI id of a golden image for stagelet machines and build workers |
Setup also takes the flags that every cloud takes. At the end, it prints the gateway’s Elastic IP. Point DNS at the gateway with an A record.
After setup
- Add
.stagely.ymlto your repository. - Add config values and secrets.
- Read the Security model before you add real secrets.