# External Storage: Store Builds in Your Own Bucket

Source: https://help.testapp.io/external-storage/

External Storage lets you keep your app builds and attachments in your own cloud storage account instead of TestApp.io's storage. You own the bucket, the keys and the data.

## Why use External Storage?

- **Data location.** Choose where your files are kept (Amazon S3 lets you pick the AWS region).
- **Company policy.** Some teams must keep build artifacts in an account they control.
- **Retention and access.** You set your own retention, logging and billing on the bucket.

## Plan requirement

External Storage is available on the **Pro** plan. On Free and Starter, the **Add New** button is disabled. See [testapp.io/pricing](https://testapp.io/pricing) for plan details.

If your team downgrades to a plan without External Storage, or a payment fails, your external storages are set to Inactive.

## Supported providers

- Amazon S3
- Google Cloud Storage
- Wasabi
- DigitalOcean Spaces
- Vultr Object Storage
- IBM Cloud Object Storage
- Backblaze B2 (beta)

For every provider you enter an **access key** and a **secret** for an S3-compatible API. For Google Cloud Storage, use an HMAC key (Cloud Storage "interoperability" access key), not a service account JSON file.

**TestApp.io creates the bucket for you.** You do not enter a bucket name. The bucket is named `testappio-` followed by a random ID. You can see it on the storage card in the Portal.

Only Amazon S3 lets you choose the region. For the other providers TestApp.io uses a fixed region, shown as **Region** when you edit the storage.

## Amazon S3

### Choose a region

When you pick Amazon S3, a **Region** list appears. Your bucket is created in the region you choose. **You cannot change the region later.** To use another region, add a new storage.

The list covers the standard AWS regions. GovCloud and China regions are not supported.

### Opt-in regions

AWS keeps some regions turned off until you enable them. The Portal marks these with "opt-in". Enable the region in your AWS account first: **Account settings > AWS Regions**. If you do not, bucket creation fails.

Opt-in regions: Africa (Cape Town), Asia Pacific (Hong Kong, Taipei, Hyderabad, Jakarta, Melbourne, Malaysia, New Zealand, Thailand), Canada West (Calgary), Europe (Zurich, Milan, Spain), Israel (Tel Aviv), Middle East (Bahrain, UAE) and Mexico (Central).

### Create an IAM user and policy

Create a dedicated IAM user for TestApp.io and an access key for it. Attach a policy like the one below. It limits the user to buckets whose names start with `testappio-`, which is the name TestApp.io gives the buckets it creates.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "TestAppIoBuckets",
      "Effect": "Allow",
      "Action": [
        "s3:CreateBucket",
        "s3:DeleteBucket",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads",
        "s3:GetBucketLocation",
        "s3:PutBucketOwnershipControls",
        "s3:PutBucketPublicAccessBlock"
      ],
      "Resource": "arn:aws:s3:::testappio-*"
    },
    {
      "Sid": "TestAppIoObjects",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:AbortMultipartUpload",
        "s3:ListMultipartUploadParts"
      ],
      "Resource": "arn:aws:s3:::testappio-*/*"
    }
  ]
}
```

What each permission is for:

- `s3:CreateBucket`: creates the bucket when you add the storage.
- `s3:PutBucketOwnershipControls` and `s3:PutBucketPublicAccessBlock`: set up the bucket so uploads work (see below).
- `s3:PutObject` and `s3:PutObjectAcl`: upload builds and attachments as public-read objects. They also upload the check file.
- `s3:DeleteObject`: delete the check file, and delete builds when you delete a release or app.
- `s3:ListBucket`, `s3:GetBucketLocation` and `s3:GetObject`: check access when you update credentials or reactivate a storage, and read objects.
- `s3:ListBucketMultipartUploads`, `s3:ListMultipartUploadParts` and `s3:AbortMultipartUpload`: large builds are uploaded in parts.
- `s3:DeleteBucket`: removes the bucket when you remove the storage in the Portal.

If you already have an older policy, the two new permissions are `s3:PutBucketOwnershipControls` and `s3:PutBucketPublicAccessBlock`.

### What TestApp.io changes on the bucket, and why

New Amazon S3 buckets reject object ACLs and block all public access by default. TestApp.io stores builds as public-read objects so that install links work for your testers. So right after it creates the bucket, TestApp.io changes these settings on **that bucket only**:

- **Object Ownership:** set to "Bucket owner preferred".
- **Block Public Access:** the two ACL options are turned off ("Block public access to buckets and objects granted through new access control lists (ACLs)" and "...through any access control lists (ACLs)").
- **Block Public Access, kept on:** the two policy options stay on. Public bucket policies remain blocked.

Then TestApp.io uploads a small file named `testappio-storage-check.txt` with a public-read ACL, and deletes it right away. This proves real uploads will work. If you see this file in the bucket, it is safe to delete.

If any step fails, TestApp.io deletes the bucket it just created, so nothing is left behind, and shows an error.

Uploaded builds and attachments are public-read objects. Anyone who has a file's URL can download it. The bucket itself cannot be listed publicly.

### Account-level Block Public Access

AWS also has a Block Public Access setting for the whole account, and organizations can add policies on top. If either blocks public ACLs, bucket-level changes are not enough and connecting fails with the "Amazon S3 rejected a test upload" error (see Troubleshooting). To fix it, open **S3 > Block Public Access settings for this account** and turn off the two ACL options. You can leave the two policy options on. Then add the storage again.

## Set up External Storage

1. Sign in to the [TestApp.io Portal](https://portal.testapp.io/) and open **Team**, then the **Storage** tab. You need permission to manage storage.
2. Click **Add New**. The **Add Storage** panel opens.
3. Select a **Provider**.
4. For Amazon S3, select a **Region**.
5. Enter the **Key** and the **Secret**. Each must be at least 10 characters.
6. Click **Add**. TestApp.io creates the bucket and checks it. This can take several seconds.
7. When it succeeds, you see "Storage successfully added" and the storage appears under **External Storage Providers** with the status **Active**.

There is no separate Validate button. The checks run when you click **Add**.

To use the storage, select it in the **Storage** field when you create an app. New builds and attachments for that app go to your bucket.

You cannot add the same key twice. If an active storage already uses the key, you see "You already created a storage with this key".

## Manage your storage

On the storage card, click the three-dot menu, then **Edit** or **Remove**.

### Deactivate and reactivate

In **Edit**, open the **Status** tab, choose **Active** or **Inactive**, and click **Update**. While a storage is inactive, it cannot be used for new apps or releases, and existing apps and releases that use it are not accessible. Your data stays in your bucket. Reactivating needs a plan that includes External Storage.

TestApp.io can also set a storage to Inactive on its own, for example when your plan changes or when it loses access to the bucket. Hover over the Inactive badge to see the reason.

### Update credentials

In **Edit**, open the **Credentials** tab, enter the new **Access Key** and **Secret Key**, and click **Update**. TestApp.io first checks that the new key can access the bucket. If the storage was inactive and the check passes, it becomes active again.

The new key needs the same permissions as the old one.

The region, endpoint and bucket cannot be edited. They are shown read-only.

### Remove a storage

Click **Remove**, then **Yes, Remove**. This cannot be undone. Removing a storage:

- **deletes the bucket and everything in it**, using your key (the key needs `s3:DeleteBucket` and `s3:DeleteObject`), and
- **deletes every app that uses this storage**, with its releases and attachments.

The dialog shows how many apps and releases will be removed. If you want to keep your files, copy them out of the bucket first. Deactivating instead of removing keeps everything in place.

## Troubleshooting

These are the messages you may see in the Add Storage panel.

### "You need to upgrade your team's plan to use this feature"

Your plan does not include External Storage. Upgrade to Pro.

### "Unable to create bucket, please check the key/secret again and make sure that this key has the permission to create & delete bucket, upload, read, list and delete objects in [region] region"

The key is wrong, or the policy does not allow `s3:CreateBucket`. For an opt-in region, the message continues: "[region] is an opt-in region, so you must also enable the region in your AWS account (Account settings > AWS Regions) before using it". Enable the region and try again.

### "Unable to prepare the bucket in [region] region, please make sure that this key also has the s3:PutBucketOwnershipControls and s3:PutBucketPublicAccessBlock permissions..."

The key could create the bucket but cannot change its ownership or public access settings. Add the two permissions from the policy above. TestApp.io has already removed the new bucket, so you can try again.

### "Amazon S3 rejected a test upload that is publicly readable in [region] region, so uploads would fail..."

Block Public Access is on for the whole AWS account, or an organization policy forbids public ACLs. See "Account-level Block Public Access" above. Also check that the key has `s3:PutObject`, `s3:PutObjectAcl` and `s3:DeleteObject`.

### "Unable to configure the bucket in [region] region, please try again. If this keeps happening, contact us"

The setup failed for a reason other than permissions. Try again. If it keeps happening, contact us.

### "Unsupported AWS region" or "This storage provider does not support choosing a region"

Pick a region from the list. Only Amazon S3 supports choosing a region.

### "Unable to access bucket with provided credentials..."

Shown when you update credentials. The new key cannot access the bucket. Check the key, the secret and the policy.

### "Unable to delete bucket, please check the key/secret again and make sure that this key has the permission to delete [bucket] bucket..."

Shown when you remove a storage. Make sure the key still has `s3:DeleteBucket` and `s3:DeleteObject` on the bucket. If you already deleted the bucket in your cloud account, contact us.

### "This storage is inactive. Please activate it before use."

Open **Team > Storage**, click **Edit** and set the status to **Active**. If it cannot be reactivated, update the credentials.

### Installs or downloads return "Access Denied"

Someone may have changed the bucket's Block Public Access or Object Ownership settings, or turned on account-level Block Public Access after setup. Restore the settings described above.

## Security notes

- Use a dedicated IAM user or key for TestApp.io. Do not reuse keys from other services.
- Scope the policy to `testappio-*` buckets, as shown above.
- TestApp.io stores your key and secret so it can upload and manage files. The Portal never shows the secret again.
- Rotate the key from time to time. Use **Edit > Credentials** to give TestApp.io the new one.
- Files are public-read, so anyone with a file's URL can download it. Do not store anything in these buckets that must stay private.
- Turn on access logging on the bucket if you want an audit trail.

---

Need help? [Contact us](https://testapp.io/contact-us) and we'll get back to you shortly.
