---
title: "Pull request previews on AWS"
description: "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."
url: https://stagely.dev/docs/setup/cloud/aws
---

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. The `stagely` repository 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:

1. It calls STS `GetFederationToken` for 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.
2. It calls ECR `GetAuthorizationToken` with 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-cidr` ranges.
- 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

```sh
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](https://stagely.dev/docs/setup/cloud.md#flags-every-cloud-takes). At the end, it prints the gateway's Elastic IP. [Point DNS at the gateway](https://stagely.dev/docs/setup/cloud.md#point-dns-at-the-gateway) with an A record.

## After setup

1. [Add `.stagely.yml`](https://stagely.dev/docs/app/stagely-yml.md) to your repository.
2. [Add config values and secrets](https://stagely.dev/docs/setup/config.md).
3. Read the [Security model](https://stagely.dev/docs/security.md) before you add real secrets.
