> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fly.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Add Object Storage

## Create a Tigris Storage Buckets

<CodeGroup>
  ```bash cmd theme={null}
  fly storage create
  ```

  ```output output theme={null}
  ? Select Organization: fly-ephemeral (fly-ephemeral)
  ? Choose a name, use the default, or leave blank to generate one:
  Your project (summer-grass-2004) is ready.

  Set one or more of the following secrets on your target app.
  BUCKET_NAME: summer-grass-2004
  AWS_ENDPOINT_URL_S3: https://fly.storage.tigris.dev
  AWS_ACCESS_KEY_ID: tid_xxxxxx
  AWS_SECRET_ACCESS_KEY: tsec_xxxxxx
  ```
</CodeGroup>

### Public buckets

By default, buckets are private. If you need to serve public assets like images or JavaScript files, create a *public bucket*:

```bash theme={null}
fly storage create --public
```

You can also make a public bucket private.

```bash theme={null}
fly storage update mybucket --private
```

Objects in a public bucket can be accessed by anyone without authentication. Public content is served from dedicated domains using the bucket name as a subdomain:

* `https://mybucket.t3.tigrisfiles.io/key-name` (primary)
* `https://mybucket.t3.tigrisbucket.io/key-name`
* `https://mybucket.t3.tigrisblob.io/key-name`

No credentials or pre-signed URLs are needed. For production use, we recommend setting up a custom domain so your public URLs stay stable:

```bash theme={null}
flyctl storage update mybucket --custom-domain assets.example.com
```

Then create a CNAME record for `assets.example.com` pointing to `mybucket.t3.tigrisbucket.io`. See the [Tigris Custom Domain docs](https://www.tigrisdata.com/docs/buckets/custom-domain/) for full setup instructions and the [Tigris Public Bucket docs](https://www.tigrisdata.com/docs/buckets/public-bucket/) for details on all available public domains.

### Object Level Access Control

By default, all objects inherit the access control settings of the bucket they are in. If a bucket is private, all objects in it are also private and vice versa.
However, you can make individual objects in a bucket public-read (or private) by setting an object level ACL on them. This lets you serve a public object from an otherwise private bucket, or a private object from an otherwise public bucket.
For details on how to set this up see the [Tigris Object ACL docs](https://www.tigrisdata.com/docs/objects/acl/)

To verify that everything has gone well, we can check whether the appropriate secrets have been set for our app:

```bash theme={null}
fly secrets list
```

## Connecting to the Bucket

The de-facto library for interacting with s3 storage is [boto3](https://pypi.org/project/boto3/), let's add it to the project:

```bash theme={null}
poetry add boto3
```

Now we can initialize the client:

```python theme={null}
import boto3

S3_URL = os.getenv("AWS_ENDPOINT_URL_S3")
svc = boto3.client('s3', endpoint_url=S3_URL)
```

<Info>
  The AWS credentials will be automatically extracted from the environment.
</Info>

Let's plug that into our app:

```python theme={null}
@app.get("/")
async def read_root():
    buckets = svc.list_buckets()
    return {"buckets": [bucket["Name"] for bucket in buckets["Buckets"]]}
```

At this point you can interact with the bucket through the `boto3` interface; refer to [the docs](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) for all the possibilities.

When you re-deploy your app you should see a list of the buckets you have access to:

```bash theme={null}
fly deploy
```

Take a look at [the gist](https://gist.github.com/fliepeltje/5be7d0a6f1ada057b1eb3aa357cd4961) of this setup to get a full picture.
