# Deploying Potato on AWS Lightsail

Source: https://www.potatoannotator.com/docs/deployment/deploy-aws

`potato deploy up --provider aws` puts an annotation study on one AWS Lightsail virtual machine for a flat $12 a month, with an HTTPS address to send your annotators. Lightsail is the AWS target to use unless it is turned off in your account. The price includes the IPv4 address and a 60 GB disk that survives restarts, and Potato needs only the `lightsail:*` and `sts:GetCallerIdentity` permissions, so it works under a restricted IAM role. This page needs Potato 2.10 or later and a config you have already run with `potato start`.

## Setting up AWS credentials

Potato reads AWS credentials the same way the `aws` command-line tool does, from `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, then `AWS_PROFILE`, then `~/.aws/credentials` and `~/.aws/config`. Install the AWS extra, sign in with the AWS CLI, and check that Potato sees the account you expect:

```bash
pip install 'potato-annotation[deploy-aws]'
aws configure                     # or: aws sso login --profile lab
potato deploy providers --verify
```

`providers --verify` asks each provider who your credentials belong to and prints the answer. If your institution uses single sign-on, pass the profile name to every deploy with `--aws-profile lab`, and Potato records it so that later commands use the same account. Potato never writes AWS credentials to disk.

If your IAM user or role is restricted, ask your administrator for `lightsail:*` and `sts:GetCallerIdentity`. The other two AWS targets need more permissions than these. The EC2 target (`--provider aws-ec2`, about $18 a month) is for accounts where Lightsail is turned off, and the ECS Express target (`--provider aws-ecs`) runs a container whose disk does not survive a restart.

## Deciding who can sign in

`potato deploy` does not change your sign-in settings, and a Lightsail address is public. If the config lets anyone register, the dry run below reports warning D003, "Anyone who finds the URL can register and annotate." For a crowdsourced study, open registration is usually intended, since the platform sends workers to the link. For named annotators, list them under `user_config.users` with `allow_all_users: false` ([Password Management](/docs/deployment/password-management)) or have them sign in with Google, GitHub or your institution ([SSO and OAuth](/docs/deployment/sso-oauth)) before you deploy.

## Checking the plan

A dry run needs no AWS account and creates nothing. It prints the preflight report, every step the deploy would take, and the estimated cost:

```bash
potato deploy up myproject/config.yaml --provider aws --dry-run
```

For a default deploy the plan creates one instance named after the task in `us-east-1`, using the 2 GB `small_3_0` bundle and Ubuntu 24.04. It attaches a static IP, so the address and its certificate survive a stop and start, and opens ports 22, 80 and 443 only. Potato listens on port 8000 behind the Caddy web server, and the deploy never opens port 8000. Potato generates an SSH deploy key for this deployment and does not use your own SSH keys.

Three flags change the defaults. `--region` takes any Lightsail region. Because Potato holds the item set in memory, a corpus too large for 2 GB needs `--size medium_3_0`, which gives 4 GB for $24 a month. `--volume-gb` adds a separate Lightsail disk at $0.10 per GB a month and keeps the task's data on it.

## Deploying

Run the same command without `--dry-run`. Potato shows the plan again and waits for you to confirm:

```bash
potato deploy up myproject/config.yaml --provider aws
```

The deploy takes several minutes while the instance boots and installs Docker. It ends by printing the URL, an `https://` address on the static IP, and the path of the admin key file, `.potato/secrets.json` beside your config. The admin key opens the [admin dashboard](/docs/features/admin-dashboard) at `/admin`. Running `up` again after you edit the config uploads the new version and restarts the task on the same instance.

Without a domain name, Let's Encrypt issues the certificate for the IP address. It lasts about six days and renews automatically. `potato deploy status myproject/config.yaml` reports a renewal that fails, so check it every few days if the study runs unattended. To use your own domain, point a DNS A record at the static IP and redeploy with `--domain annotate.example.edu`.

## Ending the study

The annotations are on the instance's disk, which survives restarts and reboots but not `destroy`. Pull them to your machine first, check them, and then remove the instance:

```bash
potato deploy pull myproject/config.yaml
potato deploy destroy myproject/config.yaml
```

`pull` writes everything the deployment collected into a new `potato-pull-<name>-<timestamp>` directory and copies the databases with SQLite's backup command. `destroy` will not run on a deployment that has never been pulled. It releases the static IP, because Lightsail bills for one that is not attached to an instance. With `--volume-gb`, `destroy --keep-data` keeps the separate disk.

## Troubleshooting

**"AWS rejected the credentials"** means the SSO session has expired or `AWS_PROFILE` names a profile that does not exist. Run `aws sso login` and deploy again.

**"Lightsail does not sell bundle …"** appears when AWS renames bundles after a price change. The error lists the bundles currently sold, and `--size` takes one of them.

**HTTPS fails for the first few minutes** because the certificate is requested once the instance is reachable. `potato deploy status` reports a certificate error separately from a server that is down.

## Further reading

- [Running Potato Locally or in the Cloud](/docs/deployment/quick-deploy), for the other cloud targets and the backup flags
- [Deploying an Annotation Server](/docs/guides/deploying-annotation-server), for choosing a host
- [AWS deployment reference](https://github.com/davidjurgens/potato/blob/master/docs/deployment/deploy-aws.md), with the exact IAM actions for each AWS target
