# Bring your own bucket (/docs/deployment/bring-your-own-bucket)



Bring your own bucket [#bring-your-own-bucket]

This guide configures customer-owned storage for RawTree Cloud. It changes the
storage destinations without moving database compute into your cloud account.
For that deployment model, see [RawTree BYOC](/docs/deployment#rawtree-byoc).

Configure a customer IAM role and separate data and backup destinations. The
API stores the storage configuration and returns bucket and path metadata.
Send API requests to `https://api.rawtree.com`.

Verify access for a cluster [#verify-access-for-a-cluster]

Prepare two S3 buckets and an IAM role. Send the proposed External ID in
`s3_storage_access`, not in `s3_storage`.

Call `POST /v1/clusters/verify-s3-access?organization=<organization>`:

```json
{
  "s3_storage_access": {
    "external_id": "rawtree-example-external-id"
  },
  "s3_storage": {
    "data": { "bucket": "acme-data", "path": "rawtree/data" },
    "backups": { "bucket": "acme-backups", "path": "rawtree/backups" },
    "role_arn": "arn:aws:iam::123456789012:role/RawTreeStorage"
  }
}
```

The response contains `verified` and `message`:

```json
{ "verified": true, "message": "<verification result>" }
```

Resolve any returned validation error before creating the cluster. The check
does not save the External ID.

The role ARN must contain a 12-digit account ID and a `role/` resource. The
external ID must contain 2–1,224 letters, numbers, or `_ + = , . @ : / -`.
The backend delegates access verification to the cluster service. The verifier
checks role trust and S3 permissions, but the backend source does not enumerate
S3 actions. Newly changed IAM policies may need several seconds to propagate.

Create a cluster with the buckets [#create-a-cluster-with-the-buckets]

Send the same `s3_storage` object with
`POST /v1/clusters?organization=<organization>`, along with the required name,
region ID, replica count, and size.
Choose an enabled region from [`GET /v1/regions`](/docs/reference/api/regions#get-v1regions).

```json
{
  "name": "analytics",
  "region_id": "11111111-1111-4111-8111-111111111111",
  "replicas": 1,
  "size": { "cpu_cores": 2, "memory_gib": 8 },
  "s3_storage_access": {
    "external_id": "rawtree-example-external-id",
    "resource_tag": "rawtree-example-resource-tag"
  },
  "s3_storage": {
    "data": { "bucket": "acme-data", "path": "rawtree/data" },
    "backups": { "bucket": "acme-backups", "path": "rawtree/backups" },
    "role_arn": "arn:aws:iam::123456789012:role/RawTreeStorage"
  }
}
```

Save `id` and `s3_storage_access` from the cluster response. These identity
values cannot change after cluster creation. Apply
`rawtree.com/cluster=<resource_tag>` to both buckets and the IAM role. The size
and replica count must match `GET /v1/clusters/sizes`.

Verify access for a database [#verify-access-for-a-database]

Database-level storage requires a cluster with S3 access configured. Call
`POST /v1/databases/verify-s3-access` with the database name and bucket settings:

```json
{
  "name": "analytics_archive",
  "s3_storage": {
    "data": { "bucket": "acme-archive-data", "path": "tables" },
    "backups": { "bucket": "acme-archive-backups", "path": "snapshots" },
    "role_arn": "arn:aws:iam::123456789012:role/RawTreeStorage"
  }
}
```

The backend loads the cluster's saved External ID. The customer role trust
policy must require that ID. Do not add `external_id` to a database request.
Tag both database buckets and the customer IAM role with
`rawtree.com/cluster=<resource_tag>` from the cluster response. The verification
response contains `verified` and `message`. If it succeeds, send the same object to
`POST /v1/databases`. Its response contains the created database:

```json
{ "database": { "name": "analytics_archive" } }
```

Save `database.name` for later requests.

> **Untested:** These steps require real AWS credentials, buckets, IAM policies,
> and an environment that supports customer-owned S3 storage.
